ALS technical notes

From FlightGear wiki
Jump to: navigation, search

Quality level mapping

The rendering quality of ALS effects is controlled by two main sliders, the landmass effects and transition effects. The transition effects slider regulates the quality of overlaid textured, while the landmass effects slider regulates all other aspects of procedural texturing, such as pixel color post-processing (dust, snow or wet terrain effects) and apparent terrain roughness (bump and parallax mapping).

In addition, specific effects for certain terrain types (water, urban, forest, agriculture, etc.) and models in the scene can be switched on separately. In some cases, this may be needed for a consistent visual impression. For example, if a dust effect is used on the terrain, the water needs to be rendered using a separate water shader, otherwise it will appear dusty as well. Likewise, if the Rayleigh haze is used in the highest quality terrain effect, then the highest quality water effect needs to be used to see the same Rayleigh scattering effect on the water.

For technical reasons (landmass and transition control the same shader code) the settings are sometimes mutually dependent, e.g., if the transition quality is set to level 6, it won't have any effect until the landmass quality is also set to level 6.

Transition

The mapping of quality to visuals of the transition slider is as follows:

Level Comments
1 Base texture scheme
2 Alternative hires airport keep effect
3
  • Base and overlay texture, runway effect (if landmass is above level 4)
  • Secondary lights on runway and airport keep
4 Base, overlay and hires texture
5 Base, overlay, hires, detail, grain, dot and rock texture (if landmass is level 6)

Landmass

Note  Quality levels 1 and 2 are reserved to represent fixed pipeline rendering and the "default renderer" in an eventually merged rendering GUI. However, ALS is currently switched on per checkbox and not by quality level, and so they do not have a function.
Level Comments
1 See note above
2 See note above
3 ALS-rendered position-differential haze and light, moonlight
4
  • Procedural snow cover on terrain
  • Procedural dust and vegetation effects
  • Wet terrain effect with approximate reflection half vector
  • Patchy fog distribution
5 Noise bump-mapping and parallax mapping of terrain
6 (Requires transition effect level 6)
  • Wet terrain effect with correct reflection half vector
  • Hires bump mapping and snow patchiness
  • Variable upper haze layer surface
  • Rayleigh haze
  • Secondary lights on all terrain types
  • Slope line and strata effects

ALS secondary lights

The ALS framework supports a generic implementation of landing lights and a searchlight which are based on a framerate-friendly computation in screen coordinates, i.e., the lights project correctly only if the light is close to the viewer (typically, that would be from cockpit view). In other words, this is not a full (and rather expensive) computation of light volumes as in Rembrandt, but a much faster test of illuminated screen areas.

Cessna 172P using generic ALS landing lights

Two landing lights and a searchlight are supported. The landing lights have a fixed position with respect to the aircraft axis (technically, with respect to the default view axis as defined in the respective view), whereas the searchlight follows any offset of the view axis in view mode, i.e., when using the mouse to look around, the searchlight will follow the motion, the landing lights will not. The lights require ALS to run above basic quality level and work on runway and airport keep above transition setting 3 and everywhere else only at highest quality setting. All lights are controlled via properties in /sim/rendering/als-secondary-lights/.

The names of the properties should be self-explanatory, if for instance if /sim/rendering/als-secondary-lights/use-searchlight is set to true, then the searchlight (which always follows the current view axis) is used.

The properties /sim/rendering/als-secondary-lights/landing-light1-offset-deg and /sim/rendering/als-secondary-lights/landing-light2-offset-deg allow to specify angular offsets for the landing light which are then not centered on the view axis. This can be used to simulate one or two landing lights set in the wings.

Secondary lights vertical offset demo

A third offset /sim/rendering/als-secondary-lights/landing-light3-offset-deg is available which allows for a vertical offset. This is especially useful for tail dagger aircraft.

Secondary lights vertical offset settings

The lights can be switched on and off from the Property Browser for any aircraft without any modifications to the aircraft definition. Implementing them correctly aircraft side thus just involves linking the landing light switches to the ALS control properties and setting the correct angular offsets. Additionally, it is recommended to switch the lights off unless in a cockpit view, as they don't project correctly for any external view.

Range, color, light cone opening angle or intensity of the lights can currently not be configured, and there are no plans to support such a feature in the near future.

All three lights will illuminate fog (if dense enough) and precipitation.

ALS generic lights illuminating dense fog

ALS specific features of the model effect

In addition to the features supported by the model-combined-deferred effect in all three renderers (normal, light, specular, environment reflection and dirt map), ALS also supports a couple of unique effects (which, if configured, will not have any effect in other renderers.

The grain texture

ALS supports a grain texture for models. This is a semi-transparent overlay texture that works just as its equivalent for terrain texturing and provides the option of generating centimeter-scale details such as rust or discoloration on a surface without having to use huge textures. An example of a surface rendered with grain texture is the image of the USS Vinson flightdeck below:

Grain and rain effects on Vinson's flightdeck

This is done using the following effect declaration inheriting from model-combined-deferred.eff as

<?xml version="1.0" encoding="UTF-8"?>

<PropertyList>

<name>flightdeck</name>
<inherits-from>Effects/model-combined-deferred</inherits-from>
<parameters>
  <grain-texture-enabled type="int">2</grain-texture-enabled>
  <grain-magnification type="float">0.1</grain-magnification>
  <rain-enabled type="int">2</rain-enabled>
  <texture n="7">
    <image>Models/Geometry/Nimitz/rust_texture.png</image>
    <type>2d</type>
    <filter>linear-mipmap-linear</filter> 
    <wrap-s>repeat</wrap-s>
    <wrap-t>repeat</wrap-t>
    <internal-format>normalized</internal-format>
  </texture>
</parameters>

</PropertyList>

The grain texture has the number 7 and needs to be enabled by

<grain-texture-enabled type="int">2</grain-texture-enabled>

If the value is set to 1, the grain texture uses the uv-mapping of the underlying surface. If that is very irregular (as in the case of the Vinson flightdeck), alternatively the grain can be mapped in xy-model coordinates if the parameter is set to 2, the uv-mapping of the base texture layer is then discarded.

The resolution of the grain texture with respect to the base coordinate layer is specified by

<grain-magnification type="float">0.1</grain-magnification>

If the number is less than 0, the grain resolution is lower than the base layer. If the parameter is greater than 0, it is higher (note that in the above case, the grain is mapped to (xy) rather than (uv), hence the base size is 1 m, so the grain texture is mapped on a 10x10 m patch on the flightdeck, for which a 1024x1024 pixel texture provides ~1 cm sized details).

The rain effect

Any number greater than 0 passed to

<rain-enabled type="int">2</rain-enabled>

enables the splash effect of raindrops on the surface if it points upward. The surface does not have to be flat for this to work, the effect checks for the surface normal automatically, see the rain enabled for the Citation Bravo.

Thorsten noted about rain splashes: "Works fine here - runway needs to get really wet though before you see them (put the environment slider to max to achieve this quickly, it will take some waiting with just rain on)."

Rain effect on the Citation Bravo


Glossy surfaces

To better treat the environment reflections on a glossy surface, ALS contains two options not present in the other renderers.


<reflection-type type="int">1</reflection-type>

This parameter can currently be set to 1 or 2 and regulates how the color of the environment reflection is merged with the color of the glossy surface. If set to 1, the shader uses a color mixing as in the other two renderers, if set to 2 it uses a technique called 'grain merge' which gives different weight to the color channels. The reason for introducing type 2 was a grey tint which changed the reflected color in a unrealistic way. With FlightGear 2017.2.0 this will be fixed and type set to 2 should be obsolete then.

<reflection-fresnel-factor type="float">0.0</reflection-fresnel-factor>

This parameter regulates how strong the Fresnel term of a glossy surface is. Many smooth surfaces reflect much more under shallow angles such that an environment reflection is not very prominent when looking under 90 degrees but dominates the visuals when looking under a grazing angle. The parameter sets the relative strength of a Fresnel reflection with respect to the basic reflection definition which is assumed to hold for vertical viewing (i.e. setting the parameter to 0.8 gives you extra glossiness under shallow angles).

The following comparison shows the default color mixing (upper left) for a reflecting white livery and for grain merge (upper right). In addition, note the enhanced Fresnel reflectivity at the tail and the cowling on the pictures. The lower row shows (left) full reflection with default color mixing and (right) for grain merge. Default color mixing is more physically correct, while grain merge is less correct but preserves white color much better.

Effect of color mixing in a reflection Effect of grain merge in reflection

Full reflection with default Full reflection with grain merge

The exhaust flame effect

Rendering proper flames of e.g. afterburners or rocket thrusters with diffuse edges is notoriously difficult - all solutions based on textured models tend to have unnaturally sharp edges. Usually the particle system is used in such cases, however at high velocities this has other issues - particles become easily separated, making flames look disrupted and detached from the exhaust.

ALS includes a dedicated procedural effect in which the flame is rendered by numerically integrating a 3-dim distribution of glowing emitters inside a bounding box (i.e. re-creates the process by which a real flame would be seen). The function which determines the emitter distribution is partially user-controlled so that a wide range of shapes can be generated.

To use the effect, first the bounding box has to be defined. Note that the shape and alignment of the bounding box needs to be standardized for the effect to work, i.e. you need to use either the default bounding box under Aircraft/Generic/Effects/Thruster/ or make a custom one which fits inside the default box at the same location in model coordinates.

To change position, size and orientation of the bounding box to match with the airplane model, translate, scale and rotate animations or model offsets can later be used.

Model definition for effect

This is a standard XML model definition that needs to specify the geometry, all animations and the effect to use. For the geometry you can use the standard Aircraft/Generic/Effects/Thruster/thrust_flame.ac or make your own. The F-15 uses a slightly different 3D model to allow for the nozzles. The 3D model needs to be big enough to contain the flame, bearing in mind that the flame is drawn as a cylinder.

<?xml version="1.0"?>
<PropertyList>

    <path>Aircraft/Generic/Effects/Thruster/thrust_flame.ac</path>
    <nopreview/>
    <animation>
        <type>scale</type>
        <property alias="/params/augmentation-alight"/>
        <x-min>0.2</x-min>
        <y-min>0.3</y-min>
        <z-min>0.3</z-min>
        <y-max>1</y-max>
        <z-max>1</z-max>
        <x-factor>0.472</x-factor>
        <y-factor>0.6</y-factor>
        <z-factor>0.6</z-factor>
    </animation>
    <!-- on the F-15 we have a property available that controls the ignition of the afterburner. If you do not wish to implement this
         remove the following animation tag -->
    <animation>
        <type>select</type>
        <object-name>Bounding_box</object-name>
        <condition>
            <greater-than>
                <property alias="/params/augmentation-ignition"/>
                <value>0.8</value>
            </greater-than>
        </condition>
    </animation>

    <effect>
        <inherits-from>Aircraft/MyAircraft/Models/Effects/my-flame</inherits-from>
        <object-name>Bounding_box</object-name>
    </effect>

</PropertyList>

Effect definition for model

To define the effect to apply to the model (as referenced in the effect section in the model XML) you need to create my-flame.eff as below. The .eff file defines the parameters that are passed into the shader and it is these that control how the flame looks.

<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
    <name>AB-flame</name>
    <inherits-from>Effects/thrust-flame</inherits-from>
    <parameters>
        <base_flame_b type="float">0.8</base_flame_b>
        <base_flame_density type="float">0.6</base_flame_density>
        <base_flame_g type="float">0.9</base_flame_g>
        <base_flame_r type="float">0.9</base_flame_r>
        <deflection_coeff type="float">0</deflection_coeff>
        <flame_color_high_b type="float">0.8</flame_color_high_b>
        <flame_color_high_g type="float">0.85</flame_color_high_g>
        <flame_color_high_r type="float">0.9</flame_color_high_r>
        <flame_color_low_b type="float">0.07</flame_color_low_b>
        <flame_color_low_g type="float">0.15</flame_color_low_g>
        <flame_color_low_r type="float">0.35</flame_color_low_r>
        <flame_radius_fraction type="float">0.8</flame_radius_fraction>
        <noise_scale type="float">0.3</noise_scale>
        <noise_strength type="float">0.2</noise_strength>
        <thrust_collimation type="float">0.1</thrust_collimation>
        <thrust_density type="float">0.7</thrust_density>
        <use_noise type="int">1</use_noise>
        <use_shocks type="int">1</use_shocks>
    </parameters>
</PropertyList>

There is also a detailed version (utilizing 3d noise and a higher sampling resolution) available. This has to be explicitly requested in a derived effect by overriding the default shader choice with the detailed version, i.e. bu inserting

 <technique n="4">
  <pass>
     <program>
         <fragment-shader n="0">Shaders/thrustflame-ALS-detailed.frag</fragment-shader>
     </program>
  </pass>
  </technique>

below the parameter section.

Note  The detailed shader uses four times as much performance at least - only use it if you really need it!

F-15 Afterburner example image

The F-15 afterburner flame shown below (at different ambient lighting based on time) uses the parameters above. The main parameters of importance, and therefore to tune, for an aircraft jet engine are the colors, the two densities, the flame radius fraction. The base density is at the start of the flame and this. The F-15 overlays a billboarded image to achieve the diamonds that are interleaved with the ALS drawn flame.


This shows the F-15 afterburner at different ambient light.

The meaning of the parameters are as follows:

    <use_shocks type="int">1</use_shocks>
    <use_noise type="int">1</use_noise>

<use_shocks> and <use_noise> are the parameters that control the random noise and the shock diamonds. Both are modestly computationally expensive, i.e. if these effects are not required it is better to switch them off by setting these values to zero.

    <flame_color_low_r type="float">0.95</flame_color_low_r>
    <flame_color_high_r type="float">1.0</flame_color_high_r>
    <base_flame_r type="float">0.8</base_flame_r>

(same for the (g,b) channels) set the color. base_flame refers to the part of the flame which is directly attached to the thruster and usually brightest. flame_color_high refers to the high density part of the flame, flame_color_low refers to the low density parts of the flame. (rgb) values always need to be [0:1]

   <thrust_density type="float">1.0</thrust_density>
   <base_flame_density type="float">0.1</base_flame_density>

determine the overall emitter density in the flame and in the base directly at the thruster respectively. There's no formal upper limit for the parameters, but a flame with density 1 already appears fairly opaque.

    <flame_radius_fraction type="float">0.8</flame_radius_fraction>

governs how much of the bounding box the flame radius at the exhaust nozzle fills. If the flame needs to expand (as for the plume of a thruster operating in vacuum) or needs to bend (as for a flame deflected by the airstream) this parameter needs to be small, if the flame is essentially cylindrical the paramater can be chosen close to 1 to minimize clipping errors. Note that this determines the size of the flame relative to the bounding box - the overall size of the flame should be adjusted by using a scale animation on the bounding box

    <noise_strength type="float">0.2</noise_strength>
    <noise_scale type="float">0.3</noise_scale>

determined how turbulent the flame looks. noise_strength [0:1] determines how prominently the noise influences the flame and noise_scale (in meters relative to the original bounding box) determines how large the visible patches of turbulence are (given that the original bounding box is not larger than 5 m in the longest direction, the parameter should probably kept between 0.1 and 5).

    <shock_frequency>1.0</shock_frequency>

influences at what distance shock diamonds appear in the flame. Useful values are perhaps between 0.2 and 5.

    <thrust_collimation type="float">0.1</thrust_collimation>

should take a value of between 0 and 1 and determines how collimated the flame is. For values > 0, the flame is widened - note that this requires a sufficiently small flame radius fraction to avoid clipping errors. For widened flames, the density is automatically lowered and shock diamonds are removed.

    <deflection_coeff type="float">0.</deflection_coeff>

should be somewhere between 0 and 0.06 and gives a lateral deflection (such as by an airstream) to the flame. Note that this re-positions the origin of the flame in the bounding box to better utilize the bounding box volume, i.e. if you need a dynamical deflection, you also need a translate animation for the bounding box.

Any of those parameters can be adjusted runtime by replacing the value with <use>/my-property</use>. Dependent on how /my-property is created it might be tied though (in particular JSBSim-computed properties are) in which case it is not picked up by the effect framework - you need to copy it via property rule to an untied property then. This allows to render dynamical changes of the flame.

For instance the Space Shuttle main engines use

<?xml version="1.0" encoding="utf-8"?>

<PropertyList>
  <name>ssme-flame</name>
  <inherits-from>Effects/thrust-flame</inherits-from>
  <parameters>
    <flame_color_low_r>0.9</flame_color_low_r>
    <flame_color_low_g>0.7</flame_color_low_g>
    <flame_color_low_b>0.5</flame_color_low_b>
    <flame_color_high_r>0.7</flame_color_high_r>
    <flame_color_high_g>0.7</flame_color_high_g>
    <flame_color_high_b>1.0</flame_color_high_b>
    <base_flame_r type="float">0.8</base_flame_r>
    <base_flame_g type="float">1.0</base_flame_g>
    <base_flame_b type="float">1.0</base_flame_b>
    <use_shocks type="int">1</use_shocks>
    <use_noise type="int">1</use_noise>
    <thrust_collimation><use>/sim/systems/various/ssme-flame-collimation</use></thrust_collimation>   
    <thrust_density><use>/sim/systems/various/ssme-flame-density</use></thrust_density>
    <base_flame_density type="float">1.0</base_flame_density>
    <shock_frequency>1.0</shock_frequency>
    <noise_strength>0.3</noise_strength>
    <noise_scale>0.1</noise_scale>
  </parameters>
</PropertyList>

to simulate the change of flame geometry in the thin upper atmosphere during ascent:

Space Shuttle main engine flames during early ascent Space Shuttle main engine flames during late ascent

Using the deflection parameter, the curved SRB separation motor flames are rendered using

<?xml version="1.0" encoding="UTF-8"?>

<PropertyList>
  <name>SRBsep-flame</name>
  <inherits-from>Effects/thrust-flame</inherits-from>
  <parameters>
    <flame_color_low_r type="float">0.95</flame_color_low_r>
    <flame_color_low_g type="float">0.55</flame_color_low_g>
    <flame_color_low_b type="float">0.1</flame_color_low_b>
    <flame_color_high_r type="float">1.0</flame_color_high_r>
    <flame_color_high_g type="float">1.0</flame_color_high_g>
    <flame_color_high_b type="float">1.0</flame_color_high_b>
    <use_shocks type="int">0</use_shocks>
    <use_noise type="int">1</use_noise>
    <thrust_collimation type="float">0.4</thrust_collimation>   
    <thrust_density type="float">1.0</thrust_density>
    <base_flame_density type="float">0.0</base_flame_density>
    <noise_strength type="float">0.7</noise_strength>
    <noise_scale type="float">0.4</noise_scale>
    <deflection_coeff type="float">-0.06</deflection_coeff>
    <flame_radius_fraction type="float">0.1</flame_radius_fraction>
  </parameters>
</PropertyList>


Space Shuttle SRB separation motor flames

Application of the effect is not limited to flames, it can also do heat blur (think a very transparent, high-noise dark emitter distribution) or vapour trails and other smoke.

By itself, the effect does however not compute ambient and diffuse light channels, i.e. for non-emissive distributions lighting is the responsibility of the user. One quick way of obtaining a correct fading of color with light in the scene is to create an untied property based on

/rendering/scene/diffuse/red

and used this to scale all color values.

Note  Do not use the full (rgb) information in the property tree for ALS, it will produce pronounced color mismatches with the rest of the scene as ALS determines light inside the shader and never uses the properties.


Rotor wash

ALS provides an interface to render rotor wash on various terrain types - currently this is implemented for the volumetric grass as well as procedural water. This interface needs to be written aircraft-side for the effect to be rendered.

Rotor wash rendered on volumetric grass

Aircraft maintainers need to set three parameters in /environment/aircraft-effects/.

The position of the downwash column needs to be specified in eye-relative coordinates by setting /environment/aircraft-effects/wash-x and /environment/aircraft-effects/wash-y. If these are zero, the air column is assumed to be right underneath the eye point, any non-zero values displace the center northward or eastward from the eye point. To work in external views, the displacement needs to be computed by e.g. a Nasal script or property rules, the same is true if the rotor is not close to the eye point.

The strength of the downwash column on the ground is set by /environment/aircraft-effects/wash-strength. The parameter influences both the radius in which the effect is visible as well as the overall strength (up to a maximum). Aircraft maintainers need to adjust strength based on parameters like altitude AGL, rotor RPM, rotor radius and whatever else might influence the downwash.

Chute animation effect

The chute animation effect is designed to provide a natural appearance of the deformation and the fluttering motion of a piece of cloth under changing stress.

Drag chute separation sequence 1 Drag chute separation sequence 2


Coordinate system

The shader assumes that the 3d mesh of the chute is oriented in a particular coordinate system. The z-axis (upward) should extend from the zero point where the various ropes come together to the canopy above through the center of the model. The canopy of the chute should therefore roughly be in the xy plane.

To position the chute correctly with the aircraft, you then need to use appropriate offsets and rotations when loading.

Effect definition for model

To define the effect to apply to the model (as referenced in the effect section in the model XML) you need to create mychute.eff as below. The .eff file defines the parameters that are passed into the shader and it is these that control the behavior.

<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
    <name>mychute</name>
    <inherits-from>Effects/chute</inherits-from>
    <parameters>
        <chute_force><use>Aircraft/MyAircraft/myforce</use></chute_force>
	<chute_projection_z>0.0</chute_projection_z>
	<chute_fold>0.0</chute_fold>
	<chute_bend>0.0</chute_bend>
    </parameters>
</PropertyList>


The meaning of these parameters is as follows:

	<chute_projection_z>0.0</chute_projection_z>

This is the distance of the branching point of the various lines leading to the canopy (at model coordinate zero) to the edge of the canopy. All transformations affecting the shape of the canopy (in particular the collapse of the canopy if the force is gone) will be applied with respect to this reference plane.

         <chute_force><use>Aircraft/MyAircraft/myforce</use></chute_force>

This is a normalized force applied to the canopy. The chute should be modeled with a deformation corresponding to a force parameter of 1 - any higher value will narrow the chute's radius, deepen the chute's deformation along the z-axis and increase the motion, any lower value will widen chute radius (compare screenshots above) and lessen its depth and slow down motion. The normal parameter range should be from [0:2], any higher or lower may look odd.

         <chute_fold>0.0</chute_fold>
	 <chute_bend>0.0</chute_bend>

These are deformation modes used to animate a chute after jettison when it is no longer pulled into shape and can flutter through the air. The first one [0:1] is a general collapse into the projection plane with random deformations around, the second one [-1:1] is a gross bending mode of the projection plane itself. Before jettison, these parameters should default to 0.

Jettison animation

A complete animated jettison sequence is quite complex and would usually require a Nasal sequence taking the chute through a deceleration trajectory (utilizing translation and rotation animations), bending ad folding oscillations and finally some unfolding as it sinks through the air. The Space Shuttle chute uses the following control loop for 10 seconds of jettison dynamics:

var drag_chute_jettison_animation = func (time) {

var dt = getprop("/sim/time/delta-sec");
time = time + dt;

# horizontal motion

var x = 0.0;

if (time > 2.0)
	{x = -16.0 + 20.0 * time;}
else
	{x = 2.0 * time + 5.0 * time * time;}


setprop("/controls/shuttle/drag-chute-dist",x);

# vertical motion

var y = 0.0;
if (time > 5.0)
	{
	y = -2.5 + 0.6 * (time-5.0) * (time-5.0);
	}
else 	
	{
	y = -0.2 * time;
	}
if (y > 12.5) {y=12.5;}

setprop("/controls/shuttle/drag-chute-down",y);

# fold

var f = 0.5 * time;
if (f> 1.0) {f = 1.0;}
if (time > 9.5)
	{f = 0.25 + 1.5 + (time-9.5);}
if (time > 6.0)
	{
 	f = f -0.3* (time-7.0); 
	if (f<0.1) {f=0.1;}
	}
setprop("/controls/shuttle/drag-chute-fold", f);

# rotate

var r = (time - 2.0) * 18.0;
if (r>90.0) {r=90.0;}
if (r<0.0) {r=0.0;}
setprop("/controls/shuttle/drag-chute-slant", r);

# bend

var b = 0;
if (time > 7.5)
	{b = 0;}
else if (time >6.0)
	{b = 0.75 - 0.5 * (time - 6.0);} 
else if (time > 4.5) 
	{b = (time - 4.5) * 0.5;}
else {b=0;}	

setprop("/controls/shuttle/drag-chute-bend", b);

if (time > 10.0) 
	{
	print("Exiting...");
	settimer (func { setprop("/controls/shuttle/drag-chute-deploy-timer", 0); }, 0.5);
	return;
	}

setprop("/test/timer", time);

settimer( func{ drag_chute_jettison_animation (time); }, 0);

}

ALS glass effect

As of FlightGear version 3.5, ALS supports a glass effect with dynamic response to the environment which can render, for instance, the splashes of raindrops on the canopy, frost or fogging.

Frost effect Raindrop splashes

The base effect properties are controlled via inheritance and the environment response run-time via properties residing in /environment/aircraft-effects. Derived effects should inherit from Effects/glass. Any surface using the glass effect will automatically register itself as transparent for use in Rembrandt.

The glass effect is primarily intended for interior views. In particular, no external fog or haze is rendered for the glass, i.e. if the effect is used in an outside view, it is the responsibility of the aircraft modeler to take care (e.g. with LOD settings or range animations) that no problems in bad visibility occur.

Why a separate effect for glass seen from inside?

Basically because the two situations are rather different. Slight dirt on the glass against the background of the sky from inside is rather prominent, against the background of the cockpit seen from outside it is not. Visuals from inside are dominated by Mie forward scattering of light, leading to a bright glare effect when looking at dirt, frost, fog or scratches close to the sun. From outside, reflected light rather than transmitted light is dominant. The reflection of any external object on the outside of the glass changes as the aircraft moves, this is not the case for the reflection of the cockpit in the glass seen from the inside.

The viewing situation is also different. From the inside, we usually do not focus the eyes on the plane of the glass but look through it, from the outside the focus of the eyes is often close to the glass surface. For something half a meter before the eye, we also need to apply a lot more resolution and details than for an object typically seen from 10+ meters from the outside.

Add to this that atmospheric fog is never relevant for glass seen from inside but for glass seen from outside, and it suddenly makes sense to use a different effect.

The recommended effect for glass surfaces seen from outside is model-combined-transparent.eff.


Rain

Rain splashes will render automatically when the weather system reports rain via environment/rain-norm. In addition, the user can set rain splashes to render via environment/aircraft-effects/ground-splash-norm (this is intended to allow splashes to be rendered e.g., for water landings of aircraft equipped with floats).

By default, the rain splashes impact from above (more precisely the +z direction in model coordinates). This may be inadequate if the aircraft is moving. However, the shader can not know what the airstream at the glass will be, so the impact vector of rain splashes has to be modeled aircraft-side and set via environment/aircraft-effects/splash-vector-x (splash-vector-y, splash-vector-z). These are likewise in model coordinates.

As long as the length of the splash vector is less than 1, just the impact angle will change, as the length of the vector increases to 2, droplets will also be visibly moving. This allows fine control of the visuals dependent on any number of factors desired. A simple Nasal snipped varying the splash vector with airspeed for the F-16 is given below (but do not mindlessly copy and expect to work for any aircraft — it won't!). This example is for normals pointing outwards, if the normals are pointing inwards the vector needs to be inverted.

var splash_vec_loop = func(){
    var airspeed = getprop("/velocities/airspeed-kt");

    # f16
    var airspeed_max = 120;

    if (airspeed > airspeed_max) {
        airspeed = airspeed_max;
    }

    airspeed = math.sqrt(airspeed / airspeed_max);

    var splash_x = -0.1 - 2 * airspeed;
    var splash_y = 0.0;
    var splash_z = 1.0 - 1.35 * airspeed;

    setprop("/environment/aircraft-effects/splash-vector-x", splash_x);
    setprop("/environment/aircraft-effects/splash-vector-y", splash_y);
    setprop("/environment/aircraft-effects/splash-vector-z", splash_z);

    settimer(func(){
        splash_vec_loop();
    }, 1);
}

Note the timing constant of the loop — running the update per-frame leads to a spurious movement of the coordinate system in which rain is rendered and spoils the effect.


Another method when using JSBSIM would be to use a combination of FCS Functions and Filters.

<system name="c172p-glass-effects">
    <channel name="rain">
        <fcs_function name="glass-effects/splashx">
            <function>
                <difference>
                    <value>-0.1</value>
                    <product>
                        <value>2.0</value>
                        <sqrt>
                            <quotient>
                                <min>
                                    <property>/velocities/airspeed-kt</property>
                                    <value>40</value>
                                </min>
                                <value>40</value>
                            </quotient>
                        </sqrt>
                    </product>
                </difference>
            </function>
        </fcs_function>
        <fcs_function name="glass-effects/splashz">
            <function>
                <difference>
                    <value>1.0</value>
                    <product>
                        <value>1.35</value>
                        <sqrt>
                            <quotient>
                                <min>
                                    <property>/velocities/airspeed-kt</property>
                                    <value>40</value>
                                </min>
                                <value>40</value>
                            </quotient>
                        </sqrt>
                    </product>
                </difference>
            </function>
        </fcs_function>
    </channel>
</system>
<?xml version="1.0" encoding="UTF-8"?>
<PropertyList>
    <filter>
        <name>splashX</name>
        <type>gain</type>
        <input>
            <property>/fdm/jsbsim/glass-effects/splashx</property>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-vector-x</property>
        </output>
    </filter>
    <filter>
        <name>splashY</name>
        <type>gain</type>
        <input>
            <value>0.0</value>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-vector-y</property>
        </output>
    </filter>
    <filter>
        <name>splashZ</name>
        <type>gain</type>
        <input>
            <property>/fdm/jsbsim/glass-effects/splashz</property>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-vector-z</property>
        </output>
    </filter>
</PropertyList>

You could reduce the above method and eliminate the "filters" by doing the following.

<?xml version="1.0"?>
<system name="c172p-glass-effects">
    <channel name="rain">
        <fcs_function name="glass-effects/airspeed-clamped-sqrt">
            <function>  
                <sqrt>
                    <quotient>
                        <min>
                            <property>/velocities/airspeed-kt</property>
                            <value>40</value>
                        </min>
                        <value>40</value>
                    </quotient>
                </sqrt>
            </function>
        </fcs_function>
        <fcs_function name="glass-effects/splashx">
            <function>
                <difference>
                    <value>-0.1</value>
                    <product>
                        <value>2.0</value>
                        <property>/fdm/jsbsim/glass-effects/airspeed-clamped-sqrt</property>
                    </product>
                </difference>
            </function>
            <output>/environment/aircraft-effects/splash-vector-x</output>
        </fcs_function>
        <fcs_function name="glass-effects/splashy">
            <function>
               <value>0.0</value>
            </function>
            <output>/environment/aircraft-effects/splash-vector-y</output>
        </fcs_function>
        <fcs_function name="glass-effects/splashz">
            <function>
                <difference>
                    <value>1.0</value>
                    <product>
                        <value>1.35</value>
                        <property>/fdm/jsbsim/glass-effects/airspeed-clamped-sqrt</property>
                    </product>
                </difference>
            </function>
            <output>/environment/aircraft-effects/splash-vector-z</output>
        </fcs_function>
    </channel>
</system>

Yet another method (currently used in the c172p) limits movement to a range of table entries. This gives the developer the ability to control the behavior even more.

<PropertyList>
    <filter>
        <name>splash-xa</name>
        <update-interval-secs type="double">0.1</update-interval-secs>
        <type>gain</type>
        <gain>1.0</gain>
        <input>
            <expression>
                <table>
                    <property>/velocities/airspeed-kt</property>
                    <entry><ind>   0 </ind><dep> -0.33 </dep></entry>
                    <entry><ind>   4 </ind><dep> -0.33 </dep></entry>
                    <entry><ind>   5 </ind><dep> -1.7  </dep></entry>
                    <entry><ind>  50 </ind><dep> -2.1  </dep></entry>
                </table>
            </expression>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-xa</property>
        </output>
    </filter>
    <filter>
        <name>splash-za</name>
        <update-interval-secs type="double">0.1</update-interval-secs>
        <type>gain</type>
        <gain>1.0</gain>
        <input>
            <expression>
                <table>
                    <property>/velocities/airspeed-kt</property>
                    <entry><ind>   0 </ind><dep>  0.82 </dep></entry>
                    <entry><ind>   4 </ind><dep>  0.82 </dep></entry>
                    <entry><ind>   5 </ind><dep> -0.17 </dep></entry>
                    <entry><ind>  50 </ind><dep> -0.35 </dep></entry>
                </table>
            </expression>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-za</property>
        </output>
    </filter>
    <filter>
        <name>splash-xr</name>
        <update-interval-secs type="double">0.1</update-interval-secs>
        <type>gain</type>
        <gain>1.0</gain>
        <input>
            <expression>
                <table>
                    <property>/engines/active-engine/rpm</property>
                    <entry><ind>    0 </ind><dep> -0.33 </dep></entry>
                    <entry><ind>  600 </ind><dep> -0.33 </dep></entry>
                    <entry><ind>  601 </ind><dep> -1.3  </dep></entry>
                    <entry><ind> 1500 </ind><dep> -1.9  </dep></entry>
                </table>
            </expression>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-xr</property>
        </output>
    </filter>
    <filter>
        <name>splash-zr</name>
        <update-interval-secs type="double">0.1</update-interval-secs>
        <type>gain</type>
        <gain>1.0</gain>
        <input>
            <expression>
                <table>
                    <property>/engines/active-engine/rpm</property>
                    <entry><ind>    0 </ind><dep>  0.82  </dep></entry>
                    <entry><ind>  600 </ind><dep>  0.82  </dep></entry>
                    <entry><ind>  601 </ind><dep>  0.127 </dep></entry>
                    <entry><ind> 1500 </ind><dep> -0.29  </dep></entry>
                </table>0
            </expression>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-zr</property>
        </output>
    </filter>
    <filter>
        <name>splash-x</name>
        <update-interval-secs type="double">0.1</update-interval-secs>
        <type>gain</type>
        <gain>1.0</gain>
        <input>
            <condition>
                <greater-than-equals>
                    <property>/velocities/airspeed-kt</property>
                    <value>5</value>
                </greater-than-equals>
            </condition>
            <property>/environment/aircraft-effects/splash-xa</property>
        </input>
        <input>
            <condition>
                <less-than>
                    <property>/velocities/airspeed-kt</property>
                    <value>5</value>
                </less-than>
            </condition>
            <property>/environment/aircraft-effects/splash-xr</property>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-vector-x</property>
        </output>
    </filter>
    <filter>
        <name>splash-y</name>
        <update-interval-secs type="double">0.1</update-interval-secs>
        <type>gain</type>
        <gain>1.0</gain>
        <input>
            <value>0.0</value>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-vector-y</property>
        </output>
    </filter>
    <filter>
        <name>splash-z</name>
        <update-interval-secs type="double">0.1</update-interval-secs>
        <type>gain</type>
        <gain>1.0</gain>
        <input>
            <condition>
                <greater-than-equals>
                    <property>/velocities/airspeed-kt</property>
                    <value>5</value>
                </greater-than-equals>
            </condition>
            <property>/environment/aircraft-effects/splash-za</property>
        </input>
        <input>
            <condition>
                <less-than>
                    <property>/velocities/airspeed-kt</property>
                    <value>5</value>
                </less-than>
            </condition>
            <property>/environment/aircraft-effects/splash-zr</property>
        </input>
        <output>
            <property>/environment/aircraft-effects/splash-vector-z</property>
        </output>
    </filter>
</PropertyList>

Frost and fogging

Frost on the canopy is rendered when environment/aircraft-effects/frost-level is set in the range from 0 to 1. Again, it is up to the aircraft developer to decide at what exterior conditions frosting should happen and whether the aircraft is equipped with heating to remove the frost again.

Fogging is controlled by environment/aircraft-effects/fog-level in the range 0 to 1. Unless a mask is used, fogging is homogeneous across the whole surface (it is really intended to be used with a mask).

Tint

Tinted glass can be easily created by changing

<glass-tint type="vec4d" n="0"> 1.0 1.0 1.0 1.0</glass-tint>

to any value desired. This will affect all effects assumed outside of the glass layer (frost and rain splashes) but not fogging inside the glass. Use primarily for development and quick tests, don't misuse the alpha value available here, it has odd side effects.

Functional masks

If the glass surface is uv-mapped and textured, it is possible to switch on a mask functionality in the inheritance via

<texture n="2">
	<image>my_mask.png</image>
	<type>2d</type>
	<filter>linear-mipmap-linear</filter>
	<wrap-s>clamp</wrap-s>
	<wrap-t>clamp</wrap-t>
	<internal-format>normalized</internal-format>
</texture>
<use-mask type="int">1</use-mask>
<overlay-color type="vec3d" n="0">1.0 1.0 1.0</overlay-color>

The red channel in my_mask.png controls the strength of fogging, full red corresponds to maximal fogging, no red to zero fogging. This allows to selectively model the position of heaters in the cockpit. Use /environment/aircraft-effects/fog-level to adjust the actual amount of fog runtime.

The green channel of my_mask.png is the amount of reduction of rain in an area. This is intended for airplanes equipped with windshield wipers to partially clear the wiped area of the rain. Whether the windshield wiper is actually on or not is controlled runtime via /environent/aircraft-effects/use-wipers (1 sets wipers to on).

The blue channel of my_mask.png is reserved for an overlay pattern which will be drawn in an optionally specified <overlay-color> vector (white in the example xml above) - the primary function is to render damage on the glass, but with a different color, also dirt or an alternative more finely controlled frost pattern can be used. The strength of the pattern can be adjusted runtime via /environment/aircraft-effects/overlay-alpha, allowing dynamical accumulation of dirt or sudden appearance of damage.

Examples for the result of a fog mask and a damage mask are shown below:

Crack pattern using a damage mask Partial fogging using a mask texture

Note that by default all the runtime switches for the mask are off / set to zero! Remember to to switch them on when testing the effect!

Mie scattering

Most glass effects show prominent Mie forward scattering as in reality, i.e. frost patterns or fogging will appear much more prominent when looking almost towards the sun than under any other angle. While the frost pattern is normally not very prominent and one is able to look through unhindered, this changes substantially when looking into the sun, at which point it almost obscures the view.

Mie scattering of low light on frost Morning sun Mie scattering on fog

For frost and fog, the strength of the effect is set automatically, but for the damage/dirt layer it is under user control at effect design time. Changing the parameter

<overlay-glare type="float">0.5</overlay-glare>

from its default value allows to adjust the strength of the glare when looking through the overlay layer close to the sun.

Internal cockpit reflection

There is support for a reflection of the cockpit interior.

Internal glass cube map reflection


This needs to be provided as a cubemap specific for the airplane and switched on via the flag <use-reflection> set to 1. The relative strength of the reflection can be optionally via <reflection-strength>. The strength of the reflection in the cockpit is dynamically adjusted for a number of factors, among them balance of direct to indirect light, the approximate amount of light falling on the surface seen in the reflection and the amount of direct sunlight falling into the eye.

If the six cubemap faces are called my_cube_map_??.png, then using an effect file called c172p-reflect.eff the inheritance looks like this:

<?xml version="1.0" encoding="utf-8"?>

<PropertyList>
  <name>c172-reflect</name>
  <inherits-from>Effects/glass</inherits-from>
  <parameters>
    <texture n="3">
      <type>cubemap</type>
      <images>
        <positive-x>my_cube_map_px.png</positive-x>
        <negative-x>my_cube_map_nx.png</negative-x>
        <positive-y>my_cube_map_py.png</positive-y>
        <negative-y>my_cube_map_ny.png</negative-y>
        <positive-z>my_cube_map_pz.png</positive-z>
        <negative-z>my_cube_map_nz.png</negative-z>
        </images>
    </texture>
    <use-reflection type="int">1</use-reflection>
    <reflection-strength type="float">1.0</reflection-strength>
  </parameters>
</PropertyList>

The inheritance call includes all surface objects that use the effect.

<effect>
   <inherits-from>Aircraft/c172p/Models/Effects/c172p-reflect</inherits-from>
   <object-name>glas</object-name>
   <object-name>rightwindow</object-name>
   <object-name>leftwindow</object-name>
</effect>


See below (Interior shading), for details on cube_map creation and orientation.

Lightmaps for internal cockpit reflection

If the cockpit is illuminated at night, the reflection map will not show this change by default. However,the glass effect supports a lightmap for the reflection, which can be used in parallel with the lightmap for the panels to show the reflection of a lit panel at night.

Since the reflection map is a cube map, the lightmap for it has to be as well, the syntax is hence

<use-reflection-lightmap type="int">1</use-reflection-lightmap>
<texture n="4">
     <type>cubemap</type>
     <images>
          <positive-x>Models/Effects/interior/reflection/light-px.png</positive-x>     
          <negative-x>Models/Effects/interior/reflection/light-nx.png</negative-x>   
          <positive-y>Models/Effects/interior/reflection/light-py.png</positive-y>     
          <negative-y>Models/Effects/interior/reflection/light-ny.png</negative-y>    
          <positive-z>Models/Effects/interior/reflection/light-pz.png</positive-z>      
          <negative-z>Models/Effects/interior/reflection/light-nz.png</negative-z>      
      </images>
</texture>

The control parameters of the lightmap otherwise parallel those of the Model-combined_effect or the model interior effect described below, i.e. up to four channels can be independently specified on the lightmap.

Troubleshooting

By default, rain and frost are mapped to the shape of the canopy using coordinate systems that adapts to the splash vector and the canopy shape. This requires no particular action aircraft-side except to provide the bare geometry of a canopy/windshield, but may not be satisfactory in all instances - in particular for near vertical cockpit side windows the scheme does poorly.

There are two alternative coordinate maps available in this case, controlled by the parameter

<surface-mapping-scheme type="int">0</surface-mapping-scheme>

If the parameter is changed to 1, the uv-mapping of the surface is used (it has to exist of course). For this to work properly, the uv-mapping needs to be sufficiently regular and roughly preserve the mapped area.

If the parameter is changed to 2, a local orthonormal system based on the normals of the surface is constructed. This no longer takes the splash vector consistently into account and works poorly for surfaces which are flat in the (xy)-plane, but gives decent result for side windows.

The HUD effect

The HUD effect is a variant of the glass effect designed to render the visuals of a head-up display (HUD) closer to real life. This only works if the HUD is custom-created via Canvas, not via the native FG HUD mechanism as the latter by-passes the effect framework.

The effect takes the same configuration options as the glass effect (i.e. it can render frost, damage, scratches, glare,...) but in addition it runs a blur over the HUD image and alters the color distribution of the projected symbols to de-saturate the bright parts in the center. The result looks less monochromatic and more like light projected onto a surface (left: bare canvas right: HUD effect)

Comparison between a bare canvas HUD (left) and the ALS HUD shader run over it (right)


Usage

The effect is used from the model file by assigning it to the surface that also carries the canvas texture of the HUD via

    <effect>
        <inherits-from>Effects/hud</inherits-from>
        <object-name>HUDImage</object-name>
    </effect>

This should usually work out of the box, but if the HUD brightness should be changeable or the glass properties adjusted, a derived effect needs to be created.

Parameters

In addition to the parameters supported by the glass shader (which, for instance in the case of rain splashes, may or may not be appropriate for a HUD, this is left at user's discretion), the HUD effect takes the following special parameters:

   <brightness>1.0</brightness>
   <sample-res>0.0006</sample-res>
   <sample-far>2.5</sample-far>

The first one is the relative brightness setting of the HUD (which should equal the alpha value assigned to the canvas image of the symbols). This parameter is used to de-saturate colors in the symbol centers.

Remaining two parameters represent the size and shape of the blur Kernel being used. The sample resolution (sample-res) determines the overall size of the blur region, the farthest sample (sample-far) how far out the tails of the blur region extend.

Note: The number of sampling steps is not computed adaptive to these parameters, if the steps get too coarse, then multiple image echoes instead of a proper blur will be generated - adjust these parameters with care (if at all).

The ALS interior model effect

Since the interior of a cockpit is a large part of what a pilot gets to see in-flight, but this interior represents a rather special situation (light is reduced as it falls through windows, it may have artificial light, there is never any fog or haze,...) ALS offers a separate effect to specifically render the interior of a plane.

This effect also includes optional features which will be available at higher quality settings of the model shader.

Interior shadows and tinted glass effect

With FlightGear version 3.5 and above, ALS now supports interior shading; i.e. the sun shining through the windows and casting shadows on the panel.

Interior shading effect

This effect is based on an opacity map - a cube map of textures which tells the renderer where the cockpit is transparent and where not. The opacity map has to be created beforehand, which allows to make it quite detailed. In this map, white stands for a completely transparent surface, black for an opaque surface, colors for tinted glass which will create a colored light spot in the cockpit, grey hues for partial shadowing allowing to paint dirt effects onto the windows, and caustics can be drawn using the alpha channel: (1-alpha) will be used as an enhancement of the light at a certain spot.

The effect is typically declared as derived by inheritance using an effect file such as c172-interior.eff which looks like this

<?xml version="1.0" encoding="utf-8"?>

<PropertyList>
  <name>c172-interior-glass</name>
  <inherits-from>Effects/model-interior</inherits-from>
  <parameters>
	<texture n="4">
	  <type>cubemap</type>
	  <images>
		 <positive-x>Models/Effects/interior/clr_px.png</positive-x>
		 <negative-x>Models/Effects/interior/clr_nx.png</negative-x>
		 <positive-y>Models/Effects/interior/clr_py.png</positive-y>
		 <negative-y>Models/Effects/interior/clr_ny.png</negative-y>
		 <positive-z>Models/Effects/interior/clr_pz.png</positive-z>
		 <negative-z>Models/Effects/interior/clr_nz.png</negative-z>
	  </images>
	</texture>
	<opacity-cube-center type="vec3d" n="0"> 0.5 0.0 0.3</opacity-cube-center>
	<opacity-cube-scale type="vec3d" n="0"> 1.5 0.5 0.7</opacity-cube-scale>
	<opacity-cube-angle type="float">0.0</opacity-cube-angle>
  </parameters>
</PropertyList>

which is called by inheritance in the model.xml file.

Imagine the opacity map as a box surrounding the cockpit and trying to closely follow its contours. The origin in the box needs to be placed into the center of the cockpit, which is what <opacity-cube-center> does. Each of the three dimensions then needs to be stretched to roughly fit the layout of the canopy, this is done by <opacity-cube-scale>.

If opacity map center and scale are wrong, you will still see shadows, but they won't match the real cockpit layout (the lightspot of a window will be seen displaced and at a different size of the real window). Thus, carefully measuring the best box layout in a 3d tool is moderately important.


Aircraft-side the effect is called as

<effect>
   <inherits-from>Aircraft/c172p/Models/Effects/c172p-interior</inherits-from>
   <object-name>Plane.010_0</object-name>
   <object-name>Plane.010_1</object-name>
   <object-name>PilotSeat</object-name>
   <object-name>CopilotSeat</object-name>
   <object-name>panel_1_1</object-name>
   <object-name>InstrumentCover.001</object-name>
   <object-name>doorint_right</object-name>
   <object-name>doorint_left</object-name>
   <object-name>doorhandleint_right</object-name>
   <object-name>doorhandleint_left</object-name>
   <object-name>Panel_0</object-name>
   <object-name>Throttle</object-name>
   <object-name>Mixture</object-name>
   <object-name>Pedestal</object-name>
   <object-name>TrimWheel</object-name>
   <object-name>ParkingBrake</object-name>
   <object-name>BackSeat</object-name>
   <object-name>FuelSelectorFace</object-name>
 </effect>

Each object that you want the shadow effect to fall on must be included in the inheritance.

Unless you supply a path, the cube_.png's need to reside in the same directory as the model.xml where you added the <effect> tag pair that calls c172-interior.eff, not where you put the aircraft_interior.eff.


The orientation of the faces of each cube have to be adjusted based on the positive and negative axis of the model.

For example, using the c172p model as a reference.

The tail, right wing and top on the model are positive inside Blender. So the cube faces are laid out as follows.

Note: Depending on the image's orientation when photographed the rotation direction will vary. This is assuming the images are being taken from the center of the cockpit looking out. Using compass headings to describe how to rotate the images.

px (tail) rotate 90 deg from N or S to W

nx (nose) rotate 90 deg from N or S to E

py (starboard/right) from N do not rotate

ny (port/left) from N rotate 180 deg to S

pz (top) rotate 90 deg from N or S to W

nz (bottom) rotate 90 deg from N or S to W

Cube map layout of the c172p


With Blender, it's pretty easy to create the cubemap.

1. Add a camera where the cubemap will be. Set the FOV to 90 degrees. 2. Set the resolution to 1024x1024 (or any other square, power-of-two size). 3. Hide all lights and windows; disable ambient lighting (set it to black) and turn off ambient occlusion. 4. Set the world color to white. 5. Enable the compositor and add an invert node. 6. Render and save the six views as you would normally. 7. Add dirt, tinted glass color and caustics by hand

This process depends on the model setup but it should work for most aircraft with a few tweaks.

Cubemap Kit for Blender

How to make an interior cubemap with the cubemap kit

See flightgear-interior-shadow-cubemap-kit for a premade kit to create the cubemap and the .eff sample file to use it.

Important Notes

Important note: Currently (May 2015) the effect does not deal gracefully with child models included into the main model with offsets and/or rotations because these introduce a different coordinate system in which the shadow does not match. The general idea of a code solution is known, but not implemented. Right now this can only be addressed by explicitly positioning child models in the *.ac file and not using any offsets when including them.

ALS flashlight

This effect is meant to imitate a hand held flashlight for use in getting instruments in the cockpit turned on in dark conditions. It is almost identical in nature to ALS searchlight only it is applied using interior-model.eff. It has two user defined light color filters that can be applied and also a user defined light radius setting.

ALS flashlight effect

To implement this effect use

<inherits-from>Effects/model-interior</inherits-from>

A full definition looks like this

<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
  <name>c172-interior</name>
  <inherits-from>Effects/model-interior</inherits-from>
  <parameters>
	<light-filter-one type="vec3d">0.5 0.5 0.5</light-filter-one>
	<light-filter-two type="vec3d">0.9 0.2 0.2</light-filter-two>
	<light-radius type="float">13</light-radius>
  </parameters>
</PropertyList>

In the example above light-filter-one is soft white light and light-filter-two is a soft red light.

After defining as above you can turn it on using the following property

/sim/rendering/als-secondary-lights/use-flashlight = 1 is light-filter-one

/sim/rendering/als-secondary-lights/use-flashlight = 2 is light-filter-two

Special note: The flashlight effect inherits model-interior, model-interior.eff is setup around the opacity cube map, it won't allow you to go without one, and in the event you try, you get an environment reflection cube map as default which happens to have green grass on it and may cast a greenish hue over applied objects. A workaround is to create a pseudo cubemap by creating a white.png 64x64 square, and creating the following structure to represent a transparent cubemap.

<texture n="4">
  <type>cubemap</type>
    <images>
      <positive-x>Aircraft/c172p/Models/Effects/interior/white.png</positive-x>
      <negative-x>Aircraft/c172p/Models/Effects/interior/white.png</negative-x>
      <positive-y>Aircraft/c172p/Models/Effects/interior/white.png</positive-y>
      <negative-y>Aircraft/c172p/Models/Effects/interior/white.png</negative-y>
      <positive-z>Aircraft/c172p/Models/Effects/interior/white.png</positive-z>
      <negative-z>Aircraft/c172p/Models/Effects/interior/white.png</negative-z>
   </images>
</texture>

Implicit lightmap

At lowest quality level, the effect supports an implicit lightmap, i.e. a color range of the basic texture can be selected and declared to act as a lightmap. This is specifically useful to render panel backlighting as in the example below:

An implicit lightmap used to simulate a backlit panel

The effect is off by default and switched on by setting

<implicit-lightmap-enabled type="int">1</implicit-lightmap-enabled>

The following parameters are available:

<implicit-lightmap-tag-color type="vec3d">1.0 1.0 1.0</implicit-lightmap-tag-color>

This sets the base color to be tagged, i.e. if the panel labels to be illuminated at night are white, the color needs to be set to white.

<implicit-lightmap-threshold-low type="float">0.5</implicit-lightmap-threshold-low>
<implicit-lightmap-threshold-high type="float">1.5</implicit-lightmap-threshold-high>

The thresholds determine how similar a color may be to the base color in order to be still illuminated. The measure is the Euklidean distance of the color vectors. The lower thresholds determines when illumination will start to fade, the higher threshold at what distance in color space illumination will no longer take place. The thresholds have to be adapted to the specific situation - the technique works best for high-contrast panels (white labels on black background) and may not work at all for low contrast panels.

<implicit-lightmap-emit-color type="vec3d">1.0 1.0 1.0</implicit-lightmap-emit-color>

The emit color specifies at which color the pixel will show if it is tagged (see above) and the implicit lightmap is on (see below) - set this to the color of the backlight.

<implicit-lightmap-intensity type="float">0.0</implicit-lightmap-intensity>

Finally, the intensity determines how brightly the illuminated pixel will shine.

Ambiance lighting can be simulated with the following tags.

<residual-ambience-r type="float"><use>/fdm/jsbsim/systems/light/cockpit-ambience-r</use></residual-ambience-r>
<residual-ambience-g type="float"><use>/fdm/jsbsim/systems/light/cockpit-ambience-g</use></residual-ambience-g>
<residual-ambience-b type="float"><use>/fdm/jsbsim/systems/light/cockpit-ambience-b</use></residual-ambience-b>

In the above example note the use of the "use" tag.

<use>/fdm/jsbsim/systems/light/cockpit-ambience-r</use>

This can be any defined property or static value.

Special note: The implicit lightmap inherits model-interior, model-interior.eff is setup around the opacity cube map, it won't allow you to go without one, and in the event you try, you get an environment reflection cube map as default which happens to have green grass on it and may cast a greenish hue over applied objects. A workaround is to create a pseudo cubemap by creating a white.png 64x64 square, and creating the following structure to represent a transparent cubemap.

<texture n="4">
  <type>cubemap</type>
    <images>
      <positive-x>Aircraft/c172p/Models/Effects/interior/white.png</positive-x>
      <negative-x>Aircraft/c172p/Models/Effects/interior/white.png</negative-x>
      <positive-y>Aircraft/c172p/Models/Effects/interior/white.png</positive-y>
      <negative-y>Aircraft/c172p/Models/Effects/interior/white.png</negative-y>
      <positive-z>Aircraft/c172p/Models/Effects/interior/white.png</positive-z>
      <negative-z>Aircraft/c172p/Models/Effects/interior/white.png</negative-z>
   </images>
</texture>

Irradiance maps

Note: This effect requires higher than minimum model shader quality setting.

In the simplest rendering schemes, diffuse and specular light are assumed to be highly directional (i.e. it illuminates lit surfaces only) while ambient light is taken to be omnidirectional.

In reality, that is hardly ever true. In a typical outside location, ambient light is mainly coming from the sky above (which is why the ground underneath a car is dark even in the absence of directional light when the sky is overcast and the sun is not visible). In an enclosed space (like a room, or an aircraft cockpit), ambient light has to fall through the windows, i.e. is mainly not coming above but falling in horizontally.

The directional dependence is not very strong though, yet it leaves measurable effects. In rendering, this can be accounted for by irradiance maps. In the following, the interior of the C-172p is rendered in the first case using omnidirectional ambient light, in the second case using an irradiance map giving the ambient light the directionality of the cabin windows, i.e. the light falls in predominantly horizontally (in addition, a grain map is superimposed to provide structure). Note how the spatial structure of the roof, looking bland when rendered in omnidirectional light is brought out by the irradiance map.

C-172p cabin interior rendered without irradiance and grain map C-172p cabin interior rendered with irradiance and grain map

Irradiance mapping is configured by the following parameters:

<irradiance-map-type type="int">2</irradiance-map-type>
<irradiance-map-strength type="float">0.5</irradiance-map-strength>

There are several pre-configured irradiance map functions which can be chosen by map type.

0: (default) omndirectional light 1: light predominantly coming from above (most appropriate for fighter cockpits) 2: light predominantly coming from the horizon (most appropriate for GA aircraft)

The strength of the irradiance map regulates how pronounced the asymmetry of the map will be. A strength of zero always makes the light omnidirectional, whereas a strength of 1 implies that no light is coming from any direction 90 degree to the main direction (i.e. for the option 2, a strength of 1.0 means that no ambient light will fall in from above or below and that hence horizontal surfaces will appear pitch black unless direct light falls on them)

Special note: The irradiance map inherits model-interior, model-interior.eff is setup around the opacity cube map, it won't allow you to go without one, and in the event you try, you get an environment reflection cube map as default which happens to have green grass on it and may cast a greenish hue over applied objects. A workaround is to create a pseudo cubemap by creating a white.png 64x64 square, and creating the following structure to represent a transparent cubemap.

<texture n="4">
  <type>cubemap</type>
    <images>
      <positive-x>Aircraft/c172p/Models/Effects/interior/white.png</positive-x>
      <negative-x>Aircraft/c172p/Models/Effects/interior/white.png</negative-x>
      <positive-y>Aircraft/c172p/Models/Effects/interior/white.png</positive-y>
      <negative-y>Aircraft/c172p/Models/Effects/interior/white.png</negative-y>
      <positive-z>Aircraft/c172p/Models/Effects/interior/white.png</positive-z>
      <negative-z>Aircraft/c172p/Models/Effects/interior/white.png</negative-z>
   </images>
</texture>
.

Explicit lightmap

Note: This effect requires higher than minimum model shader quality setting.

In addition to the implicit lightmap, it is also possible to explicitly specify a lightmap. The syntax for this exactly parallels the syntax of the Model-combined_effect which supplies lightmaps for the exterior.

For example

<texture n="3">
<image>Aircraft/MyAircraft/Effects/my_lightmap.png</image>
<type>2d</type>
<filter>linear-mipmap-linear</filter>
<wrap-s>clamp</wrap-s>
<wrap-t>clamp</wrap-t>
<internal-format>normalized</internal-format>
</texture>
<lightmap-enabled type="int">1</lightmap-enabled>
<lightmap-multi type="int">0</lightmap-multi>
<lightmap-factor type="float" n="0">1.0</lightmap-factor>
<lightmap-color type="vec3d" n="0"> 1.0 1.0 1.0 </lightmap-color>

sets up a single channel lightmap in which pixel color times the lightmap color provides the illumination. Using the multi-channel function, up to four different maps can be specified in which the (rgba) values of a pixel encode where the light falls and the associated lightmap color what color the illumination takes. Each of these maps can be switched on and off or dimmed in intensity independently.

Grain map

Note: This effect requires higher than minimum model shader quality setting.

The grain map sets a repeating hires overlay texture which can supply a very credible illusion of structure to a surface for cheap. The syntax exactly parallels that of the Model-combined_effect ALS grain feature described above.

For instance, the following

<texture n="7">
<image>Aircraft/Effects/MyGrainmap.png</image>
<type>2d</type>
<filter>linear-mipmap-linear</filter>
<wrap-s>repeat</wrap-s>
<wrap-t>repeat</wrap-t>
<internal-format>normalized</internal-format>
</texture>
<grain-texture-enabled type="int">1</grain-texture-enabled>
<grain-magnification type="float">10</grain-magnification>

sets up a grain overlay with a 10-time higher resolution over the base texture layer.

ALS procedural lights

The ALS framework supports procedurally generated lights (for instance aircraft position lights, strobe lights,... ) for aircraft and models. Note that the effect produces the visuals of the lights themselves, but they do not illuminate their surroundings.

Why procedural lights?

Traditionally aircraft position lights have been implemented as billboarded emissive textures. This technique is limited in scope, as it has difficulty producing a directional light or changes in the light appearance (for instance glare) with changed scene lighting. The ALS procedural lights are designed to give visuals consistent with the auto-generated scene lights (runway lighting, street lights) and support configurable directionality, glare and even the illumination of fog by the lights.


Basic implementation

The base of the light is a texture quad which has Effects/procedural-light.eff assigned. Start from including the template provided into your model as

 <model>
  <path>/Models/Effects/procedural_light.xml</path>
        <offsets>
            <x-m> 0 </x-m>
            <y-m> 0 </y-m>
            <z-m> 2 </z-m>
        </offsets>
 </model>

and configure from there according to your own needs.

Do not billboard the light via animation! The GLSL code will take care of the billboarding. Use a scale animation to change the size of the light, do not change the quad (the *.ac) itself in size, the shader code requires its geometry to be fixed.

The lights are currently not supported in the default or Rembrandt renderer and will simply be invisible in these frameworks.

Configuring the light

Make your own derived effect inheriting from Effects/procedural-light.eff and assign it in the xml wrapper of the light.

  • the base light color can be set via
    <light_color_base_r type="float">1.0</light_color_base_r>
    <light_color_base_g type="float">0.0</light_color_base_g>
    <light_color_base_b type="float">0.0</light_color_base_b>

These take the normal set of values ranging from 0 to 1.

  • how the light appears at full intensity in the center can be set via
    <light_color_center_r type="float">1.0</light_color_center_r>
    <light_color_center_g type="float">1.0</light_color_center_g>
    <light_color_center_b type="float">1.0</light_color_center_b>

The idea behind this is that lights give a better visual appearance if their center is rendered visually brighter and de-saturated, but this occurs only if the light is at full intensity.

There are no sanity checks done, you can assign any center color you like even if this makes no sense - you can change center color or darken it, so use with care.

  • to runtime change the light intensity, assign
    <intensity_scale type="float">1.0</intensity_scale>

to a property and change that property in the range from 0 to 1. Eg:

     <intensity_scale>
       <use>/rendering/nav-lights-factor</use>
     </intensity_scale>


  • for directional lights, you need to assign a pointing vector in aircraft coordinates into which the light cone points and set the directionality flag to true to activate the computations. This is done via:
    
    <pointing_x type="float">-1.0</pointing_x>
    <pointing_y type="float">0.0</pointing_y>
    <pointing_z type="float">0.0</pointing_z>
    <is_directional type="bool">true</is_directional>

It would be customary to pass a normalized vector, but if you don't know how to do it, the shader will do it for you. By assigning properties to the pointing coordinates, the light direction can be changed runtime.

Do not use a rotation animation to change the light direction - it won't work. Make the pointing vector properties and change them to your needs.

  • fading of a directional light away from the pointing axis is controlled by passing angles (or rather sines of angles).
    
    <inner_angle type="float">0.2</inner_angle>
    <outer_angle type="float">0.4</outer_angle>
    <zero_angle type="float">0.7</zero_angle>
    <outer_gain type="float">0.5</outer_gain>

Up to inner_angle, the light will have full intensity. From inner to outer angle the light will linearly fade to outer_gain. From outer angle to zero angle, the light intensity will linearly fade to zero.

  • to get an automatic strobe effect you can set
    
    <is_strobe type="bool">true</is_strobe>

to true. If you want your own strobe pattern, generate a strobe function and use intensity_scale instead to pass it to the shader.

Complete example

Here's the left nav light of the C-172p:

    
<?xml version="1.0" encoding="utf-8"?>
<PropertyList>

    <name>procedural-light-nav-left</name>
    <inherits-from>Effects/procedural-light</inherits-from>

    <parameters>
        <light_color_base_r type="float">1.000</light_color_base_r>
        <light_color_base_g type="float">0.320</light_color_base_g>
        <light_color_base_b type="float">0.320</light_color_base_b>
        <light_color_center_r type="float">1.0</light_color_center_r>
        <light_color_center_g type="float">1.0</light_color_center_g>
        <light_color_center_b type="float">1.0</light_color_center_b>
        <intensity_scale type="float">1.0</intensity_scale>

        <!-- Arc is 110 deg, is 55 deg per side, giving 35 deg from wing -->
        <pointing_x type="float">1.0</pointing_x>
        <pointing_y type="float">0.7002075382097097</pointing_y>
        <pointing_z type="float">0.0</pointing_z>

        <is_directional type="bool">true</is_directional>
        <is_strobe type="bool">false</is_strobe>

        <!-- Angles are 0.0 at 0 deg from pointing direction, 1.0 at
             90/-90 deg, and 0.0 at 180/-180 deg.

             For left navigation light we use -0/-35 .. +110/+145 for
             the inner/outer range. This gives an arc of 110/180 deg,
             or 55/90 deg from center.

             Value = sin(angle in degrees)

             0.8191520442889918 = 55 deg (* 2 = 110 deg inner angle)
             1.0000 = 90 deg (* 2 = 180 deg outer angle)
        -->
        <inner_angle type="float">0.8191520442889918</inner_angle>
        <outer_angle type="float">1.0</outer_angle>
        <zero_angle type="float">0.982547593563</zero_angle>
        <outer_gain type="float">0.1</outer_gain>
    </parameters>

</PropertyList>

Examples

The Cessna 182 by Heiko Schulz and Gilberto Agostinho with procedural lights activated:

(To percieve the effect well, you probably have to darken your room and switch to fullscreen)

Cessna 182 with procedural lights.jpg

Move dynamically the procedural light

The position of a procedural light is declared in the <model> section, within the <PropertyList> XML code, for example:

  <model>
      <name>procedural-light-gear-white-left</name>
      <path>Effects/lights/procedural_light_gear_white.xml</path>
      <offsets>
          <x-m> -3.08</x-m>
          <y-m> -0.21 </y-m>
          <z-m> -1.18 </z-m>
      </offsets>
  </model>

The tag procedural-light-gear-white-left is very important because is the object name. This name will be used as the reference object.
Next, we can define an animation in the same xml file:

  <animation>
      <type>rotate</type>
      <object-name>GearFrontalDoor</object-name>
      <object-name>procedural-light-gear-white-left</object-name>
      <property>gear/gear/position-norm</property>
      <axis>
          <x1-m>  -3.00240</x1-m>
          <y1-m>  1.0</y1-m>
          <z1-m>  -1.02906</z1-m>
          <x2-m>  -3.00240</x2-m>
          <y2-m>  -1.0</y2-m>
          <z2-m>  -1.0290</z2-m>
      </axis>
      <interpolation>
          <entry><ind>0.0</ind><dep>90</dep></entry>
          <entry><ind>0.25</ind><dep>90</dep></entry>
          <entry><ind>0.50</ind><dep>60</dep></entry>
          <entry><ind>0.75</ind><dep>30</dep></entry>
          <entry><ind>1.0</ind><dep>0</dep></entry>
      </interpolation>
  </animation>

The procedural light object code is this:

  <object-name>procedural-light-gear-white-left</object-name>

ALS fuselage shadow effect

ALS supports the manipulation of a (simplified) ground shadow. This effect uses an existing simplified shadow animation configuration and by default uses the gear-agl-m property to calculate the ground placement of that simplified shadow.

The system, techniques for using it, and several working examples are on this extensive forum thread about the ALS shadow effects.

ALS Shadow

The effect can be applied easily in any aircraft that report the gear-agl-m property by simply adding a declaration inheriting from shadow.eff.

If the aircraft does not support the gear-agl-m property, (notably JSBSim), you need to create a Property Rule to pass a supported AGL data to the gear-agl-m property. In this case we use altitude-agl-ft converted to meters using a property rule configuration file.

For the first example we'll use the "supported gear-agl-m" method for aircraft that don't require a property rule.

Simply add the following inheritance declaration after you declare your shadow animation statement.

<effect>
    <inherits-from>Effects/shadow</inherits-from>
    <object-name>name_of_the_shadow_object</object-name>
</effect>

In the non-ALS simplified shadow code you normally use a "translate" animation to position the shadow on the ground. When using the ALS method you must either remove, comment out, or apply a condition to restrict its use to non ALS shadow applications, because ALS is responsible for computing the shadows ground position.

Here is how we apply a condition to the translation to limit it effect to non-ALS application only.

<!--Translate to ground level  -->
  <animation>
   <type>translate</type>
   <object-name>shadow</object-name>
   <condition>
    <not>
      <property>/sim/rendering/shaders/skydome</property>
    </not>
   </condition>
   <property>/position/altitude-agl-ft</property>
   <factor>-0.3048</factor>
   <center>
     <x-m>0</x-m>
     <y-m>0</y-m>
     <z-m>0</z-m>
   </center>
   <axis>
     <x>0</x>
     <y>0</y>
     <z>1</z>
   </axis>
 </animation>

A complete shadow animation block looks like this.

<PropertyList>

  <animation>
    <!-- opaque objects -->
    <!-- transparent objects -->
    <object-name>shadow</object-name>
    <type>select</type>
    <condition>
      <not>
        <property>/sim/rendering/rembrandt/enabled</property>
      </not>
    </condition>
  </animation>
  
  <effect>
    <inherits-from>Effects/shadow</inherits-from>
    <object-name>shadow</object-name>
  </effect>

  <animation>
    <type>noshadow</type>
    <object-name>shadow</object-name>
  </animation>

  <!-- pitch -->
  <animation>
    <type>rotate</type>
    <object-name>shadow</object-name>
    <property>/orientation/pitch-deg</property>
    <factor>-1.0</factor>
    <center>
      <x-m>0</x-m>
      <y-m>0</y-m>
      <z-m>0</z-m>
    </center>
    <axis>
      <x>0</x>
      <y>1</y>
      <z>0</z>
    </axis>
  </animation>

  <!-- roll -->
  <animation>
    <type>rotate</type>
    <object-name>shadow</object-name>
    <property>/orientation/roll-deg</property>
    <factor>1.0</factor>
    <center>
      <x-m>0</x-m>
      <y-m>0</y-m>
      <z-m>0</z-m>
    </center>
    <axis>
      <x>1</x>
      <y>0</y>
      <z>0</z>
    </axis>
  </animation>

  <!--Translate to ground level  -->
  <animation>
   <type>translate</type>
   <object-name>shadow</object-name>
   <condition>
    <not>
      <property>/sim/rendering/shaders/skydome</property>
    </not>
   </condition>
   <property>/position/altitude-agl-ft</property>
   <factor>-0.3048</factor>
   <center>
     <x-m>0</x-m>
     <y-m>0</y-m>
     <z-m>0</z-m>
   </center>
   <axis>
     <x>0</x>
     <y>0</y>
     <z>1</z>
   </axis>
  </animation>

</PropertyList>

For aircraft requiring a Property Rule there are a couple extra steps.

In aircraft-set.xml you add the property rule call to the "configuration file" in between the PropertyList/sim/systems tag pairs.

	
<property-rule n="101">
  <name>gear_agl-m</name>
  <path>Aircraft/c172p/Systems/gearAGL.xml</path>
</property-rule>

Note: the n="101" needs to be =+100 as -100 is reserved for system wide property rules. In the above example 101 was used because 100 was already being used by a previous property rule definition.

The "configuration file" normally goes in /Systems. In this example we named the configuration file /Systems/gearAGL.xml.

Here is the complete /Systems/gearAGL.xml configuration file.

<?xml version="1.0"?>

<PropertyList>
  <filter>
    <type>gain</type>
    <gain>0.3048</gain>
    <input>/position/altitude-agl-ft</input>
    <reference>6</reference>
    <output>/position/gear-agl-m</output>
  </filter>
</PropertyList>

What the configuration file does is convert the input of altitude-agl-ft to the output of (((altitude-agl-ft)-6)*0.3048) or (convert ft-6 to m) and pass it to /position/gear-agl-m.

See Input and reference properties or values <input> and <reference>.

ALS 3d shadow volume effect

What might be considered the next level of realism using a relatively "cheap" shadow effect is to use a 3d volume shadow.

ALS 3d Shadow Volume

The procedure to implement the ALS shadow volume is similar to the ALS-shadow above with a few modifications.

First you have to have a 3d shadow volume model of the aircraft you are applying the effect to. This model should be a low poly model that is optimized to be as little of a footprint as possible.

A property rule is required, same as above. This is used to generate the above ground altitude in meters, (altitude-agl-m). The "altitude-agl-m" is used to place the shadow close to the ground in real time at an FDM rate of execution.

Note  Do not confuse "altitude-agl-m" with the other type of non-3d ALS-shadow property of "gear-agl-m". The shaders involved use these two different properties respectively for the two different effect. They are not interchangeable.
<?xml version="1.0"?>
<PropertyList>
   <filter>
      <type>gain</type>
      <gain>0.3048</gain>
      <input>/position/altitude-agl-ft</input>
      <reference>1.0</reference>
      <output>/position/altitude-agl-m</output>
   </filter>
</PropertyList>

The other difference from the ALS-shadow above is that it uses a different inherited effect. Instead of

<effect>
    <inherits-from>Effects/shadow</inherits-from>
    <object-name>shadow</object-name>
 </effect>

you use

<effect>
    <inherits-from>Effects/shadow-vol</inherits-from>
    <object-name>shadow</object-name>
  </effect>

To make sure there is no duplicate 3d model shown in other renderers besides ALS it is also important to use a different select animation:

 <animation>
    <!-- hide 3d shadow in non ALS mode -->
    <object-name>shadow</object-name>
    <type>select</type>
    <condition>
      <property>/sim/rendering/shaders/skydome</property>
    </condition>
  </animation>

Related content