Materials in Oolite

From Elite Wiki
Revision as of 21:20, 10 February 2010 by Eric Walch (talk | contribs) (Added scripting category)
This page applies to Oolite 1.69 and later.

Traditionally, the surface appearance of ships in Oolite has been defined entirely by textures specified in the ship’s model file and the smooth attribute. Test release 1.67 added support for shaders, and test release 1.69 expands this to a more flexible material model. Under the new model, the texture file names in model files have been repurposed as material keys. A material key specifies an entry in the ship’s material dictionary, specified in the ship’s shipdata.plist entry. For backwards compatibility, if no material of a given name is found, a material is generated using the material key as a diffuse texture name – providing the same functionality as previous versions of Oolite.


The materials and shaders dictionary

The material dictionary is actually specified as two separate dictionaries in the shipdata.plist entry. The first is called materials, the second shaders. If shader support is available and enabled, the entries in shaders are used in preference to those in materials. Otherwise, shaders is ignored. The entries in the two dictionaries take exactly the same form, however; it is possible to specify a shader in the materials dictionary, and it will simply be ignored if shader support is not available. The materials and shaders dictionaries, merged together, are known as “the material dictionary”.


Material attributes dictionaries

The material dictionary is a property list dictionary whose keys are material keys as defined above, and whose values are material attribute dictionaries. Currently-supported material attribute dictionary keys are listed here; “texture specifier” and similar are defined below.

General material attribues
Name Type Description
ambient colour specifier The ambient colour of the entity, that is, the colour reflected evenly in all directions by starlight. By default, this is the same as diffuse.
diffuse colour specifier Overall diffuse colour of the entity; that is, the colour reflected in a “non-shiny” way on the sunlit side of the entity. If both diffuse and diffuse_map are specified, the texture is “dyed” with this colour. Default: white (1.0, 1.0, 1.0, 1.0).
diffuse_map texture specifier A texture specifying the basic colour of the material. This is a “normal” texture, the same as you get without a material dictionary. If not specified, the material key will be used. To specify no texture, use an empty string for diffuse_map. Ignored when using shaders.
emission colour specifier The emitted (glow) colour of the material. Default: black (0.0, 0.0, 0.0, 1.0).
shininess  integer The “tightness” of specular reflect, ranging from 0 to 128. 0 means no specular highlight. Default: 0 in shader mode and 10 in non-shader mode. From Oolite 1.74, it will default to 128 if specular_map is used and 10 otherwise.
specular colour specifier The specular (shiny reflection) colour of the material. This is ignored if shininess is not 1 or more. Default: black (0.0, 0.0, 0.0, 1.0) in shader mode and (0.2, 0.2, 0.2, 1.0) in non-shader mode. From Oolite 1.74, it will default to white (1.0, 1.0, 1.0, 1.0) if specular_map is used and (0.2, 0.2, 0.2, 1.0) otherwise.
textures array of texture specifiers Textures to use in shader program.


The following attributes are only supported if shader support is active:

Shader material attribues
Name Type Description
emission_map texture specifier A texture specifying emission (glow) colours. If both emission and emission_map are specified, the values of the emission_map will be multiplied by emission. Emission mapping is available in Oolite 1.70 and later with shaders enabled, for materials with no custom shader.
emission_and_illumination_map texture specifier A texture whose RGB channels act like emission_map, and whose alpha channel acts much like illumination_map, adding white light, for materials with no custom shader.
illumination_map texture specifier A texture specifying local illumination colour, which is added to incoming light. (This is useful for things like hull spotlights.) Illumination mapping is available in Oolite 1.70 and later with shaders enabled, for materials with no custom shader.
fragment_shader file name name of GLSL fragment program to use.
normal_map texture specifier A texture modifying normals. The red, green and blue channels are the x, y and z coordinates of tangent-space normals, and the alpha channel is ignored.
normal_and_parallax_map texture specifier Like normal_map, except that the alpha channel is includes a depth value. If specified, normal_map is ignored. The parallax component is ignored in simple shader mode.
parallax_bias number A value added to the parallax component of normal_and_parallax_map, after multiplying by parallax_scale (default: 0).
parallax_scale number A scale factor for the parallax component of normal_and_parallax_map (default: 0.01).
specular_map texture specifier A texture modifying specular and shininess. The colour channels of the specular value are multiplied by the specular_map colour values, and the shininess value is scaled by the alpha channel of the specular_map. Specular mapping is available in Oolite 1.70 and later with shaders enabled, for materials with no custom shader.
textures array of texture specifiers Textures to use in shader program.
uniforms dictionary of uniform variable specifiers uniform variables to use in shader program.
vertex_shader file name name of GLSL vertex program to use.


Illumination mapping vs. emission mapping

This image demonstrates the distinction between illumination mapping (top row) and emission mapping (bottom row).

Emission-vs-illumination.png

Colour specifiers

Colour specifiers are used to declare colours in material attribute dictionaries, and in other contexts such as lasers in shipdata.plist. Colour specifiers take any of several forms:


Named colours

The simplest form of colour specifier is a string containing a colour name. The following names are supported:

Colour names
Name RGBA value
blackColor 0, 0, 0, 1
darkGrayColor 1/3, 1/3, 1/3, 1
lightGrayColor 1/6, 1/6, 1/6, 1
whiteColor 1, 1, 1, 1
grayColor 1/2, 1/2, 1/2, 1
redColor 1, 0, 0, 1
greenColor 0, 1, 0, 1
blueColor 0, 0, 1, 1
cyanColor 0, 1, 1, 1
yellowColor 1, 1, 0, 1
magentaColor 1, 0, 1, 1
orangeColor 1, 1/2, 0, 1
purpleColor 1/2, 0, 1/2, 1
brownColor 0.6, 0.4, 0.2, 1
clearColor 0, 0, 0, 0


RGBA tuples

Colours may also be specified in RGB or RGBA (red, green, blue, alpha [transparency]). RGB or RGBA tuples may be specified as arrays or as strings separated by spaces. The values may range from 0 to 1 or 0 to 255. (If no value is greater than 1, 0–1 is assumed, otherwise 0–255.) Examples:

  • <string>1 1 0</string> <!-- yellow -->
  • <string>0 128 0 128</string> <!-- medium green at 50% opacity -->
  • <array><real>1</real> <real>0</real> <real>0</real></array> <!-- red -->


RGBA dictionaries

Just in case you’re unsure of the order the RGB[A] components come in, you can use a dictionary:

<dict>
    <key>blue</key> <real>0.8</real>
    <key>green</key> <real>0.8</real>
</dict>

Accepted keys are red, green, blue and alpha (or opacity), all optional (default: 0.0 for red, green and blue, 1.0 for alpha).


HSBA dictionaries

But wait, there’s more! Colours may also be specified as dictionaries in the HSB colour space. Accepted keys are hue (hue angle in degrees, required), saturation, brightness (or value) and alpha (or opacity). The default for the optional keys is 1.0.

<dict>
    <!-- Pale magenta -->
    <key>hue</key> <real>300</real>
    <key>saturation</key> <real>0.3</real>
</dict>

Texture specifiers

Texture specifiers are used to, er, specify textures. The simplest form of texture specifier is a string: <string>ahruman-example-texture.png</string>. The very simplest texture specifier is the empty string, <string/> (or "" in OpenStep style property lists), meaning no texture. More complex texture specifiers are dictionaries, which can be used to specify a number of texture options.

Texture specifier attributes
Name Type Description
anisotropy real The degree of anisotropic filtering to use. Anisotropic filtering is used to reduce blurriness of MIP mapped textures shown at an angle (and thus only applies to textures whose min_filter is mipmap or default). The anisotropy setting is multiplied by the maximum anisotropy level specified by the system, and anisotropic filtering is used only if the result is greater than 1. (For example, on a GeForce FX 5200 card, the maximum level is 8, so an anisotropy value of 0.5 means an effective anisotropic filtering strength of 4. The precise meaning of this number is hardware-dependant. Fun, eh?) Anisotropic filtering is disabled in Reduced Detail mode and is not available on all hardware. Default: 0.5.
mag_filter string Specifies how to scale up the texture when it is near the camera. Either of:
  • nearest (use nearest-neighbour sampling, which produces a pixellated effect)
  • linear (use linear interpolation, which produces a blurry effect).

Default: linear.

min_filter string Specifies how to scale down the texture when it is far from the camera. One of:
  • nearest: nearest-neighbour sampling; generally very ugly, but sometimes useful for shaders using textures for special purposes.
  • linear: efficient, but rather ugly for textures with significant contrast or details.
  • mipmap: use trilinear MIP map filtering to improve scaling quality; generally also fast and least ugly, but uses 1/3 more memory, potentially slowing down systems with limited RAM or video memory.
  • default: linear in reduced detail mode, otherwise mipmap.

Default: default.

name string The file name of the texture to use. Must be a PNG image, in the Textures folder of Oolite or an OXP.
no_shrink boolean Indicates that Oolite should not scale the texture down, except to meet hardware requirements. Default: false.
repeat_s boolean Whether Oolite should repeat the texture in the s (or u) co-ordinate, rather than clamping it. Default: false.
repeat_t boolean Whether Oolite should repeat the texture in the t (or v) co-ordinate, rather than clamping it. Default: false.
texture_LOD_bias real tweak factor for MIP mapping (which does not apply to other min_filter modes). MIP mapping involves a trade-off between scaling artefacts and blurriness; the LOD (level of detail) bias affects this. Positive values mean more blur, negative ones mean more artefacts. The default value is −0.25 (mostly so people won’t say “hey, why is everything blurrier in the new Oolite?”). Texture LOD bias is not available on all hardware.


Uniform specifiers

Main article: Shaders in Oolite: uniforms

A uniform specifier is used to specify game information to pass to a shader program. As with texture specifiers, the simple case is a string. For instance, <key>uHullHeatLevel</key> <string>hullHeatLevel</string> will bind a shader’s uHullHeatLevel uniform to a ship’s hullHeatLevel property. Additional properties can be specified in a dictionary.

Texture specifier attributes
Name Type Description
asMatrix boolean For a binding uniform whose bound property is of type quaternion, setting asMatrix to true causes the value to be converted to a rotation matrix. If asMatrix is false, the quaternion will be converted to a vector, whose w, x, y and z values match those of the quaternion. For any other type of uniform, clamped is ignored. Default: true.
binding string The entity property to bind to, for a binding uniform. Ignored for other types of uniform.
clamped boolean For a binding uniform whose bound property is of type float, setting clamped to true causes the value to be restricted to the range 0 to 1, inclusive. For a binding of type int, causes the value to be set to either 0 or 1; 1 indicates that the underlying property has any value other than 0. For any other type of uniform, asMatrix is ignored. Default: false.
normalized boolean For a binding uniform whose bound property is of type vector, setting normalized to true causes the value to be normalized, that is, scaled to a length of 1. For any other type of uniform, normalized is ignored. The spelling normalised is also accepted. Default: false.
type  string One of:
  • binding: indicates that the binding property specifies an entity property to be set dynamically each time the shader is used.
  • float or real: indicates that the value property is an floating-point number.
  • int or integer: indicates that the value property is an integer, or whole number.
  • texture: indicates that the value property is an integer specifying a texture unit, that is, an index in the textures array. This lets you select an alternative to the standard texture names tex0, tex1 etc.

Default: float.

value number  The value to use for a float, integer or texture uniform. Ignored for binding uniforms.


Material dictionary example

Here is an example from the Oolite 1.69 version of Freaky Thargoids:

<key>shaders</key>
<dict>
    <key>thargoid_redux.png</key>
    <dict>
        <key>textures</key>
        <array>
            <string> thargoid_redux.png</string>
            <dict>
                <key>name</key>
                <string>thargoid-glow.png</string>
                <key>mag_filter</key>
                <string>linear</string>
            </dict>
        </array>
        <key>fragment_shader</key>
        <string>freaky_thargoid.fragment</string>
        <key>vertex_shader</key>
        <string>freaky_thargoid.vertex</string>
        <key>uniforms</key>
        <dict>
            <key>uTime</key>
            <string>timeElapsedSinceSpawn</string>
            <key>uReciprocalFrequency</key>
            <dict>
                <key>value</key>
                <real>1.0</real>
            </dict>
        </dict>
    </dict>
</dict>

…or, in OpenStep format:

shaders =
{
    "thargoid_redux.png" =  // replace this texture
    {
        textures =
        (
            "thargoid_redux.png",  // passed as tex0
            {
                name = "thargoid-glow.png";  // passed as tex1
                mag_filter = linear;        // no mip-map (added purely for demonstration purposes)
            }
        );
        fragment_shader = "freaky_thargoid.fragment";
        vertex_shader = "freaky_thargoid.vertex";
        uniforms =
        {
            // Alternatively, universalTime may be used, in which case all thargoids will pulse in sync.
            "uTime" = "timeElapsedSinceSpawn";
            "uReciprocalFrequency" = { value = 1.0; };
        };
    };
};

This means that, if shaders are available, the material named “thargon_redux.png” will use a vertex and fragment shader pair called freaky_thargoid.vertex and freaky_thargoid.fragment; that the textures used by these shaders are thargon-base.png and thargon-glow.png, and that the latter should use linear magnification filtering; and that the shaders use two uniform variables, uTime and uReciprocalFrequency. uTime is bound to the property timeElapsedSinceSpawn, while uReciprocalFrequency is a constant float with the value 1.0. A similar configuration is used for the tharglet, but uReciprocalFrequency is set to 1.5. This allows the same texture to be used by two different ships, animating at different speeds.