1
0
Fork 0

Implement instancing for scenery objects

This commit implements instancing for scenery objects by introducing a
OBJECT_INSTANCED verb in stg files. Instances can be defined by their
positions, rotations and scales. The framework also supports extending
object instancing with custom effects, including an additional colorization
effect that allows for setting the colors for each instance of the same
model.

Detailed commit history can be found at [1], and mailing list discussions
at [2].

[1] https://sourceforge.net/u/fahimdalvi/fgdata/ci/feat/scenery-object-instancing/~/tree/
[2] https://sourceforge.net/p/flightgear/mailman/flightgear-devel/thread/7381A03F-BF5B-45E7-AAF3-5288B7DEDFFA%40gmail.com/#msg37617087

Squashed commit of the following:

commit 2af16b3dfa859eff8b9d96e1b9256e8d872d28c5
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Feb 24 10:24:12 2024 +0300

    Update documentation for instancing

    Add details on optional parameters and the order of transformations.

commit f0fda25c196fbb46063abdd30be63ebbc4285841
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Feb 24 10:15:17 2024 +0300

    Add low-spec shaders for object instancing.

commit bcf6e15523750ea9d1578a48de736073a37ea2bd
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Fri Feb 16 14:54:26 2024 +0300

    Add documentation

commit a4b889b40543f70c4e9c9d6002f853547c6fb373
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Tue Feb 13 17:32:11 2024 +0300

    Use header file defined indices for vertex attributes

    Vertex attributes were hardcoded across the code, this commit changes
    the implementation to use constants defined in the header file. Also
    change the indices used to be compatible with the future HDR pipeline.

commit 8120544e276161f57dd1aeda645ef52684eab7a7
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Feb 10 22:26:55 2024 +0300

    Clean up shader code

    Use *.glsl library code to minimize duplication across object-instancing*
    shaders. Rename variables to follow Shader Style Guide:
    https://wiki.flightgear.org/Shader_Style_Guide

commit ed864379621c060860c68d46cab054f8f1161f25
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Feb 3 22:23:11 2024 +0300

    Remove unused fragment shader.

commit be80516fff660764dad245cd6389e7616d8d807c
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Feb 3 22:21:15 2024 +0300

    Add support for multiple effects and add a colored instancing effect.

    This commit adds shaders and a corresponding effect for colored
    instanced objects using the new custom attributes provided by the
    corresponding SimGear commit.

commit f758ad00b08578052b815bdf138dae5b3a0e026c
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Feb 3 16:31:15 2024 +0300

    Remove unnecessary inclusion of vertex shader in inherited effect

commit 26fa5ecc8d825ed6902c6a6934a82749839cc9cc
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Wed Oct 18 21:14:18 2023 +0300

    Fix rotation to match normal scenery objects

commit 2a47c3164085d5821944f53a02a55c3bea9ca4ac
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Wed Oct 18 21:13:41 2023 +0300

    Reduce instancing effect to bare minimum

commit c24d4fdbfc295872ec6f47a8f8e002dfa301fd83
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Oct 14 11:30:34 2023 +0300

    Properly handle model/texture/effects

    Simgear:
    Previously, the model was being loaded without any options, this is
    now fixed. *ac models have their effect instantiated manually, while
    *xml models require an explicit <effect> tags. Also, the already
    loaded model node is edited (for adding vertex attributes, statesets
    etc) instead of creating a new node (for deferred texture loading and
    other stuff to work).

    FGData:
    Reverted object-instancing effect to match model-default more closely
    to make texturing work.

commit 17e74fc4cc421c3aa9f08e5bbcd08d8a5b450655
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Sep 9 14:07:35 2023 +0300

    Implement model-default effects for instanced objects.

    Not perfect yet, normal models are quite a bit darker, and we are loading
    the model using OSG directly, instead of SGModelLib.

commit d4734690745bde0652e578174e18ddf4fe799000
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Apr 23 14:52:36 2022 +0300

    Implemented instance rotation and scaling

    This commit adds support for optional per-instance rotation and
    scaling, defaulting to no scaling and rotation if none is specified.

commit dd7256818becb21695487385fe5d4b7fcd33a61e
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Fri Mar 18 09:19:24 2022 +0300

    Implemented texture handling

    The code assumes there is a single texture, and this is applied
    to the single drawable geometry created for instancing. This commit
    breaks texture-less object support for instancing.

commit 547e051459c91ef0abe1b9fe583f0aed59720135
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Sat Feb 26 14:25:57 2022 +0300

    Moved random color generation to shader

commit ab927a8555ca615d2fe51c9fc87b9fb5f6198c3f
Author: Fahim Imaduddin Dalvi <dalvifahim@gmail.com>
Date:   Tue Feb 22 18:32:10 2022 +0300

    Proof of Concept instancing support for scenery objects.
This commit is contained in:
Fahim Imaduddin Dalvi 2024-02-24 11:11:12 +03:00
parent 27618cd36d
commit c0e1e31674
12 changed files with 995 additions and 0 deletions

View file

@ -23,6 +23,7 @@ Contents ----------------------------------------------------------------------
3.11 LINEAR_FEATURE_LIST
3.12 OBJECT_LIGHT
3.13 LIGHT_LIST
3.14 OBJECT_INSTANCED
4 model manager ("/models/model")
4.1 static objects
@ -645,6 +646,54 @@ Where:
- X,Y,Z are the cartesian coordinates of the light. +X is South and +Y is East.
- The rest of the parameters are the same as OBJECT_LIGHT (Section 3.12)
3.14 OBJECT_INSTANCED
---------------------
Defines a model and properties to place multiple instances of the object
Example:
OBJECT_INSTANCED Models/Commercial/tower-glass-dark-blue.ac instances.txt default -129.00115321 -8.98951092 0
Syntax:
OBJECT_INSTANCED <object-path> <filename> <effect-path> <lon> <lat> <elev-m>
Where:
- <object-path> is the path to an object relative to the data directory (same as OBJECT_SHARED). Models used should be raw models (e.g. *.ac) and not *.xml's, and should contain as few objects as possible (recommendation is single object per model), using a texture atlas for representing different semantic parts in the model instead of splitting them into various objects.
- <filename> is the name of the file containing instance positions and other properties
- <effect-path> is the name of the effect that will be applied. See below for currently available options.
- <lon>, <lat>, <elev-m> defines the position relative to which all the instances will be placed (similar to BUILDING_LIST)
The referenced <filename> (in the example instances.txt) contains lines defining one instance per line:
<X> <Y> <Z> <hdg> <pitch> <roll> <scale>
Where:
- X, Y, Z are the cartsian coordiates of the instance, +X is South and +Y is East.
- (optional) hdg, pitch and roll define the rotation of the instance, matching OBJECT_SHARED exactly
- (optional) scale is a multiplier for the scale of the instance, with a value of 1 indicate no scaling
Each line will therefore contain 3 (position only), 6 (position+rotation), 4 (position+scale) or 7 (all properties) parameters. In the case of both rotation and scaling, the scaling is applied before rotation.
If the effect being used supports custom attributes, the line *must contain* 4 additional custom parameters:
<X> <Y> <Z> <hdg> <pitch> <roll> <scale> <custom-attrib-1> <custom-attrib-2> <custom-attrib-3> <custom-attrib-4>
Again, the intermediate rotation and scaling parameters are optional and can be skipped if the instance does not require any rotation/scaling, leading to 7 (position+custom), 10 (position+rotation+custom), 8 (position+scale+custom) or 11 (all properties) parameters.
The function of custom attributes will differ from effect to effect, see below.
### Instancing Effects
There are two types of effects supported by the instancing framework:
- Effects with default parameters
- "default": The default instancing effect described above which does basic instance position, rotation and scaling
- "Effects/object-instancing": Same as "default"
- Effects with additional custom parameters
- "Effects/object-instancing-colored": Requires four additional custom attributes, which are the colors <red> <green> <blue> <factor> respectively. The instance's color/texture will be blended with the (r,g,b) values provided based on the given factor. A factor of 0 implies keeping the original color, while a factor of 1 replaces the original color with the provided (r,g,b) completely.
Note: Implementing new effects with additional custom parameters requires a small source-side change in ObjectInstanceBin.hxx
4 model manager ("/models/model") --------------------------------------------

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
<name>Effects/object-instancing-colored</name>
<inherits-from>Effects/object-instancing</inherits-from>
<technique n="5">
<pass>
<program>
<vertex-shader n="0">Shaders/object_instancing_colored.vert</vertex-shader>
<fragment-shader n="0">Shaders/object_instancing_colored.frag</fragment-shader>
<fragment-shader n="100">Shaders/model_ALS_base.glsl</fragment-shader>
<attribute>
<name>instance_position</name>
<index>6</index>
</attribute>
<attribute>
<name>instance_rotation_and_scale</name>
<index>7</index>
</attribute>
<attribute>
<name>instance_custom_attrib</name>
<index>10</index>
</attribute>
</program>
</pass>
</technique>
<technique n="11">
<pass>
<program n="0">
<vertex-shader n="0">Shaders/object_instancing_colored_lowspec.vert</vertex-shader>
<fragment-shader n="1">Shaders/object_instancing_colored_lowspec.frag</fragment-shader>
<attribute>
<name>instance_position</name>
<index>6</index>
</attribute>
<attribute>
<name>instance_rotation_and_scale</name>
<index>7</index>
</attribute>
<attribute>
<name>instance_custom_attrib</name>
<index>10</index>
</attribute>
</program>
</pass>
</technique>
</PropertyList>

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
<name>Effects/object-instancing</name>
<inherits-from>Effects/model-default</inherits-from>
<technique n="5">
<pass>
<program>
<vertex-shader>Shaders/object_instancing.vert</vertex-shader>
<vertex-shader>Shaders/object_instancing.glsl</vertex-shader>
<vertex-shader>Shaders/shadows-include.vert</vertex-shader>
<vertex-shader>Shaders/generic_ALS_base.glsl</vertex-shader>
<attribute>
<name>instance_position</name>
<index>6</index>
</attribute>
<attribute>
<name>instance_rotation_and_scale</name>
<index>7</index>
</attribute>
</program>
</pass>
</technique>
<technique n="11">
<pass>
<program n="0">
<vertex-shader>Shaders/object_instancing_lowspec.vert</vertex-shader>
<vertex-shader>Shaders/object_instancing.glsl</vertex-shader>
<vertex-shader>Shaders/shadows-include.vert</vertex-shader>
<attribute>
<name>instance_position</name>
<index>6</index>
</attribute>
<attribute>
<name>instance_rotation_and_scale</name>
<index>7</index>
</attribute>
</program>
</pass>
</technique>
</PropertyList>

View file

@ -0,0 +1,221 @@
// -*-C++-*-
#version 120
// Duplicated from generic-ALS-base.vert, but in the form of
// library functions that can be used by other shader cpde
// Shader that uses OpenGL state values to do per-pixel lighting
//
// The only light used is gl_LightSource[0], which is assumed to be
// directional.
//
// Diffuse colors come from the gl_Color, ambient from the material. This is
// equivalent to osg::Material::DIFFUSE.
// Haze part added by Thorsten Renk, Oct. 2011
#define MODE_OFF 0
#define MODE_DIFFUSE 1
#define MODE_AMBIENT_AND_DIFFUSE 2
// The constant term of the lighting equation that doesn't depend on
// the surface normal is passed in gl_{Front,Back}Color. The alpha
// component is set to 1 for front, 0 for back in order to work around
// bugs with gl_FrontFacing in the fragment shader.
varying vec4 diffuse_term;
varying vec3 normal;
varying vec3 relPos;
varying vec4 ecPosition;
varying float yprime_alt;
varying float mie_angle;
uniform int colorMode;
uniform float hazeLayerAltitude;
uniform float terminator;
uniform float terrain_alt;
uniform float avisibility;
uniform float visibility;
uniform float overcast;
uniform float ground_scattering;
uniform float moonlight;
// This is the value used in the skydome scattering shader - use the same here for consistency?
const float EarthRadius = 5800000.0;
const float terminator_width = 200000.0;
float earthShade;
float light_func (in float x, in float a, in float b, in float c, in float d, in float e)
{
// use the asymptotics to shorten computations
if (x < -15.0) {
return 0.0;
}
return e / pow((1.0 + a * exp(-b * (x-c)) ),(1.0/d));
}
void render_ALS_base(in vec3 position)
{
vec4 light_diffuse;
vec4 light_ambient;
vec3 shadedFogColor = vec3(0.55, 0.67, 0.88);
vec3 moonLightColor = vec3 (0.095, 0.095, 0.15) * moonlight;
//float yprime_alt;
float yprime;
float lightArg;
float intensity;
float vertex_alt;
float scattering;
// this code is copied from default.vert
ecPosition = gl_ModelViewMatrix * vec4(position, gl_Vertex.w);
// Project to world space
gl_Position = gl_ModelViewProjectionMatrix * vec4(position, gl_Vertex.w);
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
gl_TexCoord[2] = gl_TextureMatrix[2] * gl_MultiTexCoord2;
vec4 ambient_color, diffuse_color;
if (colorMode == MODE_DIFFUSE) {
diffuse_color = gl_Color;
ambient_color = gl_FrontMaterial.ambient;
} else if (colorMode == MODE_AMBIENT_AND_DIFFUSE) {
diffuse_color = gl_Color;
ambient_color = gl_Color;
} else {
diffuse_color = gl_FrontMaterial.diffuse;
ambient_color = gl_FrontMaterial.ambient;
}
// here start computations for the haze layer
// we need several geometrical quantities
// first current altitude of eye position in model space
vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);
// and relative position to vector
relPos = position.xyz - ep.xyz;
// unfortunately, we need the distance in the vertex shader, although the more accurate version
// is later computed in the fragment shader again
float dist = length(relPos);
// altitude of the vertex in question, somehow zero leads to artefacts, so ensure it is at least 100m
vertex_alt = max(position.z,100.0);
scattering = ground_scattering + (1.0 - ground_scattering) * smoothstep(hazeLayerAltitude -100.0, hazeLayerAltitude + 100.0, vertex_alt);
// branch dependent on daytime
if (terminator < 1000000.0) // the full, sunrise and sunset computation
{
// establish coordinates relative to sun position
vec3 lightFull = (gl_ModelViewMatrixInverse * gl_LightSource[0].position).xyz;
vec3 lightHorizon = normalize(vec3(lightFull.x,lightFull.y, 0.0));
// yprime is the distance of the vertex into sun direction
yprime = -dot(relPos, lightHorizon);
// this gets an altitude correction, higher terrain gets to see the sun earlier
yprime_alt = yprime - sqrt(2.0 * EarthRadius * vertex_alt);
// two times terminator width governs how quickly light fades into shadow
// now the light-dimming factor
earthShade = 0.6 * (1.0 - smoothstep(-terminator_width+ terminator, terminator_width + terminator, yprime_alt)) + 0.4;
// parametrized version of the Flightgear ground lighting function
lightArg = (terminator-yprime_alt)/100000.0;
// directional scattering for low sun
if (lightArg < 10.0) {
mie_angle = (0.5 * dot(normalize(relPos), normalize(lightFull)) ) + 0.5;
} else {
mie_angle = 1.0;
}
light_diffuse.b = light_func(lightArg, 1.330e-05, 0.264, 3.827, 1.08e-05, 1.0);
light_diffuse.g = light_func(lightArg, 3.931e-06, 0.264, 3.827, 7.93e-06, 1.0);
light_diffuse.r = light_func(lightArg, 8.305e-06, 0.161, 3.827, 3.04e-05, 1.0);
light_diffuse.a = 1.0;
light_diffuse = light_diffuse * scattering;
light_ambient.r = light_func(lightArg, 0.236, 0.253, 1.073, 0.572, 0.33);
light_ambient.g = light_ambient.r * 0.4/0.33;
light_ambient.b = light_ambient.r * 0.5/0.33;
light_ambient.a = 1.0;
// correct ambient light intensity and hue before sunrise
if (earthShade < 0.5) {
//light_ambient = light_ambient * (0.7 + 0.3 * smoothstep(0.2, 0.5, earthShade));
intensity = length(light_ambient.xyz);
light_ambient.rgb = intensity * normalize(mix(light_ambient.rgb, shadedFogColor, 1.0 -smoothstep(0.4, 0.8,earthShade) ));
light_ambient.rgb = light_ambient.rgb + moonLightColor * (1.0 - smoothstep(0.4, 0.5, earthShade));
intensity = length(light_diffuse.xyz);
light_diffuse.rgb = intensity * normalize(mix(light_diffuse.rgb, shadedFogColor, 1.0 -smoothstep(0.4, 0.7,earthShade) ));
}
// the haze gets the light at the altitude of the haze top if the vertex in view is below
// but the light at the vertex if the vertex is above
vertex_alt = max(vertex_alt,hazeLayerAltitude);
if (vertex_alt > hazeLayerAltitude) {
if (dist > 0.8 * avisibility) {
vertex_alt = mix(vertex_alt, hazeLayerAltitude, smoothstep(0.8*avisibility, avisibility, dist));
yprime_alt = yprime -sqrt(2.0 * EarthRadius * vertex_alt);
}
} else {
vertex_alt = hazeLayerAltitude;
yprime_alt = yprime -sqrt(2.0 * EarthRadius * vertex_alt);
}
}
else // the faster, full-day version without lightfields
{
//vertex_alt = max(gl_Vertex.z,100.0);
earthShade = 1.0;
mie_angle = 1.0;
if (terminator > 3000000.0)
{
light_diffuse = vec4 (1.0, 1.0, 1.0, 0.0);
light_ambient = vec4 (0.33, 0.4, 0.5, 0.0);
} else {
lightArg = (terminator/100000.0 - 10.0)/20.0;
light_diffuse.b = 0.78 + lightArg * 0.21;
light_diffuse.g = 0.907 + lightArg * 0.091;
light_diffuse.r = 0.904 + lightArg * 0.092;
light_diffuse.a = 1.0;
light_ambient.r = 0.316 + lightArg * 0.016;
light_ambient.g = light_ambient.r * 0.4/0.33;
light_ambient.b = light_ambient.r * 0.5/0.33;
light_ambient.a = 1.0;
}
light_diffuse = light_diffuse * scattering;
yprime_alt = -sqrt(2.0 * EarthRadius * hazeLayerAltitude);
}
// default lighting based on texture and material using the light we have just computed
diffuse_term = diffuse_color* light_diffuse;
vec4 constant_term = gl_FrontMaterial.emission + ambient_color *
(gl_LightModel.ambient + light_ambient);
// Super hack: if diffuse material alpha is less than 1, assume a
// transparency animation is at work
if (gl_FrontMaterial.diffuse.a < 1.0)
diffuse_term.a = gl_FrontMaterial.diffuse.a;
else
diffuse_term.a = gl_Color.a;
// Another hack for supporting two-sided lighting without using
// gl_FrontFacing in the fragment shader.
gl_FrontColor.rgb = constant_term.rgb; gl_FrontColor.a = 1.0;
gl_BackColor.rgb = constant_term.rgb; gl_BackColor.a = 0.0;
}

278
Shaders/model_ALS_base.glsl Normal file
View file

@ -0,0 +1,278 @@
// -*-C++-*-
#version 120
// Duplicated from model-ALS-base.frag, but in the form of
// library functions that can be used by other shader code
varying vec4 diffuse_term;
varying vec3 normal;
varying vec3 relPos;
varying vec4 ecPosition;
varying float yprime_alt;
varying float mie_angle;
uniform float visibility;
uniform float avisibility;
uniform float scattering;
uniform float terminator;
uniform float terrain_alt;
uniform float hazeLayerAltitude;
uniform float overcast;
uniform float eye_alt;
uniform float cloud_self_shading;
uniform float air_pollution;
uniform float landing_light1_offset;
uniform float landing_light2_offset;
uniform float landing_light3_offset;
uniform int quality_level;
uniform int tquality_level;
uniform int use_searchlight;
uniform int use_landing_light;
uniform int use_alt_landing_light;
const float EarthRadius = 5800000.0;
const float terminator_width = 200000.0;
float alt;
float eShade;
float fog_func (in float targ, in float alt);
float rayleigh_in_func(in float dist, in float air_pollution, in float avisibility, in float eye_alt, in float vertex_alt);
float alt_factor(in float eye_alt, in float vertex_alt);
float light_distance_fading(in float dist);
float fog_backscatter(in float avisibility);
vec3 rayleigh_out_shift(in vec3 color, in float outscatter);
vec3 get_hazeColor(in float light_arg);
vec3 searchlight();
vec3 landing_light(in float offset, in float offsetv);
vec3 filter_combined (in vec3 color) ;
float getShadowing();
vec3 getClusteredLightsContribution(vec3 p, vec3 n, vec3 texel);
float luminance(vec3 color)
{
return dot(vec3(0.212671, 0.715160, 0.072169), color);
}
float light_func (in float x, in float a, in float b, in float c, in float d, in float e)
{
x = x - 0.5;
// use the asymptotics to shorten computations
if (x > 30.0) {return e;}
if (x < -15.0) {return 0.0;}
return e / pow((1.0 + a * exp(-b * (x-c)) ),(1.0/d));
}
// this determines how light is attenuated in the distance
// physically this should be exp(-arg) but for technical reasons we use a sharper cutoff
// for distance > visibility
void render_model_ALS_base(in vec4 texel)
{
vec3 shadedFogColor = vec3(0.55, 0.67, 0.88);
// this is taken from default.frag
vec3 n;
float NdotL, NdotHV, fogFactor;
vec4 color = gl_Color;
vec3 lightDir = gl_LightSource[0].position.xyz;
vec3 halfVector = gl_LightSource[0].halfVector.xyz;
vec4 fragColor;
vec4 specular = vec4(0.0);
float intensity;
float effective_scattering = min(scattering, cloud_self_shading);
eShade = 1.0 - 0.9 * smoothstep(-terminator_width+ terminator, terminator_width + terminator, yprime_alt);
vec4 light_specular = gl_LightSource[0].specular * (eShade - 0.1);
// If gl_Color.a == 0, this is a back-facing polygon and the
// normal should be reversed.
n = (2.0 * gl_Color.a - 1.0) * normal;
n = normalize(n);
NdotL = dot(n, lightDir);
if (NdotL > 0.0) {
float shadowmap = getShadowing();
color += diffuse_term * NdotL * shadowmap;
NdotHV = max(dot(n, halfVector), 0.0);
if (gl_FrontMaterial.shininess > 0.0)
specular.rgb = (gl_FrontMaterial.specular.rgb
* light_specular.rgb
* pow(NdotHV, gl_FrontMaterial.shininess)
* shadowmap);
}
color.a = diffuse_term.a;
// This shouldn't be necessary, but our lighting becomes very
// saturated. Clamping the color before modulating by the texture
// is closer to what the OpenGL fixed function pipeline does.
color = clamp(color, 0.0, 1.0);
float dist = length(relPos);
vec3 secondary_light = vec3 (0.0,0.0,0.0);
if ((quality_level > 5) && (tquality_level > 5)) {
if (use_searchlight == 1) {
secondary_light += searchlight();
}
if (use_landing_light == 1) {
secondary_light += landing_light(landing_light1_offset, landing_light3_offset);
}
if (use_alt_landing_light == 1) {
secondary_light += landing_light(landing_light2_offset, landing_light3_offset);
}
if (dist > 2.0) {// we don't want to light the cockpit...
color.rgb +=secondary_light * light_distance_fading(dist);
}
}
fragColor = color * texel + specular;
fragColor.rgb += getClusteredLightsContribution(ecPosition.xyz, n, texel.rgb);
float lightArg = (terminator-yprime_alt)/100000.0;
vec3 hazeColor = get_hazeColor(lightArg);
// Rayleigh color shift due to in-scattering
if ((quality_level > 5) && (tquality_level > 5)) {
float rayleigh_length = 0.5 * avisibility * (2.5 - 1.9 * air_pollution)/alt_factor(eye_alt, eye_alt+relPos.z);
float outscatter = 1.0-exp(-dist/rayleigh_length);
fragColor.rgb = rayleigh_out_shift(fragColor.rgb,outscatter);
float rShade = 1.0 - 0.9 * smoothstep(-terminator_width+ terminator, terminator_width + terminator, yprime_alt + 420000.0);
float lightIntensity = length(hazeColor * effective_scattering) * rShade;
vec3 rayleighColor = vec3 (0.17, 0.52, 0.87) * lightIntensity;
float rayleighStrength = rayleigh_in_func(dist, air_pollution, avisibility/max(lightIntensity,0.05), eye_alt, eye_alt + relPos.z);
fragColor.rgb = mix(fragColor.rgb, rayleighColor,rayleighStrength);
}
// here comes the terrain haze model
float delta_z = hazeLayerAltitude - eye_alt;
float mvisibility = min(visibility, avisibility);
if (dist > 0.04 * mvisibility) {
alt = eye_alt;
float transmission;
float vAltitude;
float delta_zv;
float H;
float distance_in_layer;
float transmission_arg;
// angle with horizon
float ct = dot(vec3(0.0, 0.0, 1.0), relPos)/dist;
// we solve the geometry what part of the light path is attenuated normally and what is through the haze layer
if (delta_z > 0.0) { // we're inside the layer
if (ct < 0.0) { // we look down
distance_in_layer = dist;
vAltitude = min(distance_in_layer,mvisibility) * ct;
delta_zv = delta_z - vAltitude;
} else { // we may look through upper layer edge
H = dist * ct;
if (H > delta_z) {
distance_in_layer = dist/H * delta_z;
} else {
distance_in_layer = dist;
}
vAltitude = min(distance_in_layer,visibility) * ct;
delta_zv = delta_z - vAltitude;
}
} else { // we see the layer from above, delta_z < 0.0
H = dist * -ct;
if (H < (-delta_z)) { // we don't see into the layer at all, aloft visibility is the only fading
distance_in_layer = 0.0;
delta_zv = 0.0;
} else {
vAltitude = H + delta_z;
distance_in_layer = vAltitude/H * dist;
vAltitude = min(distance_in_layer,visibility) * (-ct);
delta_zv = vAltitude;
}
}
// ground haze cannot be thinner than aloft visibility in the model,
// so we need to use aloft visibility otherwise
transmission_arg = (dist-distance_in_layer)/avisibility;
float eqColorFactor;
//float scattering = ground_scattering + (1.0 - ground_scattering) * smoothstep(hazeLayerAltitude -100.0, hazeLayerAltitude + 100.0, relPos.z + eye_alt);
if (visibility < avisibility) {
transmission_arg = transmission_arg + (distance_in_layer/visibility);
// this combines the Weber-Fechner intensity
eqColorFactor = 1.0 - 0.1 * delta_zv/visibility - (1.0 -effective_scattering);
} else {
transmission_arg = transmission_arg + (distance_in_layer/avisibility);
// this combines the Weber-Fechner intensity
eqColorFactor = 1.0 - 0.1 * delta_zv/avisibility - (1.0 -effective_scattering);
}
transmission = fog_func(transmission_arg, alt);
// there's always residual intensity, we should never be driven to zero
if (eqColorFactor < 0.2) eqColorFactor = 0.2;
// Mie-like factor
if (lightArg < 10.0) {
intensity = length(hazeColor);
float mie_magnitude = 0.5 * smoothstep(350000.0, 150000.0, terminator-sqrt(2.0 * EarthRadius * terrain_alt));
hazeColor = intensity * ((1.0 - mie_magnitude) + mie_magnitude * mie_angle) * normalize(mix(hazeColor, vec3 (0.5, 0.58, 0.65), mie_magnitude * (0.5 - 0.5 * mie_angle)) );
}
// high altitude desaturation of the haze color
intensity = length(hazeColor);
hazeColor = intensity * normalize (mix(hazeColor, intensity * vec3 (1.0,1.0,1.0), 0.7* smoothstep(5000.0, 50000.0, alt)));
// blue hue of haze
hazeColor.x = hazeColor.x * 0.83;
hazeColor.y = hazeColor.y * 0.9;
// additional blue in indirect light
float fade_out = max(0.65 - 0.3 *overcast, 0.45);
intensity = length(hazeColor);
hazeColor = intensity * normalize(mix(hazeColor, 1.5* shadedFogColor, 1.0 -smoothstep(0.25, fade_out,eShade) ));
// change haze color to blue hue for strong fogging
//intensity = length(hazeColor);
hazeColor = intensity * normalize(mix(hazeColor, shadedFogColor, (1.0-smoothstep(0.5,0.9,eqColorFactor))));
// reduce haze intensity when looking at shaded surfaces, only in terminator region
float shadow = mix( min(1.0 + dot(normal,lightDir),1.0), 1.0, 1.0-smoothstep(0.1, 0.4, transmission));
hazeColor = mix(shadow * hazeColor, hazeColor, 0.3 + 0.7* smoothstep(250000.0, 400000.0, terminator));
// don't let the light fade out too rapidly
lightArg = (terminator + 200000.0)/100000.0;
float minLightIntensity = min(0.2,0.16 * lightArg + 0.5);
vec3 minLight = minLightIntensity * vec3 (0.2, 0.3, 0.4);
hazeColor *= eqColorFactor * eShade;
hazeColor.rgb = max(hazeColor.rgb, minLight.rgb);
// determine the right mix of transmission and haze
fragColor.rgb = mix(hazeColor + secondary_light * fog_backscatter(mvisibility), fragColor.rgb,transmission);
}
fragColor.rgb = filter_combined(fragColor.rgb);
gl_FragColor = fragColor;
}

View file

@ -0,0 +1,70 @@
// Yaw
void rotation_matrix_H(in float sinRz, in float cosRz, out mat3 rotmat)
{
rotmat = mat3( cosRz, -sinRz, 0.0,
sinRz, cosRz, 0.0,
0.0 , 0.0 , 1.0);
}
// Pitch
void rotation_matrix_P(in float sinRy, in float cosRy, out mat3 rotmat)
{
rotmat = mat3( cosRy, 0.0 , sinRy,
0.0 , 1.0 , 0.0,
-sinRy, 0.0 , cosRy);
}
// Roll
void rotation_matrix_R(in float sinRx, in float cosRx, out mat3 rotmat)
{
rotmat = mat3( 1.0 , 0.0 , 0.0,
0.0 , cosRx , -sinRx,
0.0 , sinRx , cosRx);
}
void scale_matrix(in float scale, out mat3 scalemat)
{
scalemat = mat3(scale, 0.0 , 0.0 ,
0.0 , scale, 0.0 ,
0.0 , 0.0 , scale);
}
/* apply_instance_transforms
* Takes the position, normal of an vertex, and position/rotation/scale of
* one instance, and transforms the original position and normal of the
* vertex to match it's instanced version.
*/
void apply_instance_transforms(inout vec3 position, inout vec3 normal, in vec3 instance_position, in vec4 instance_rotation_and_scale)
{
// Handle rotation and scaling
mat3 ScaleMat;
scale_matrix(instance_rotation_and_scale.w, ScaleMat);
mat3 RotMatH;
mat3 RotMatP;
mat3 RotMatR;
float hdg = instance_rotation_and_scale.x;
float pitch = instance_rotation_and_scale.y;
float roll = instance_rotation_and_scale.z;
// if (roll > 90.0 || roll < -90.0) {roll = -roll;}
float cosRx = cos(radians(-roll));
float sinRx = sin(radians(-roll));
float cosRy = cos(radians(-pitch));
float sinRy = sin(radians(-pitch));
float cosRz = cos(radians(-hdg));
float sinRz = sin(radians(-hdg));
rotation_matrix_R(sinRx, cosRx, RotMatR);
rotation_matrix_P(sinRy, cosRy, RotMatP);
rotation_matrix_H(sinRz, cosRz, RotMatH);
// Transform position using scaling and rotation matrices
position = RotMatH * RotMatP * RotMatR * ScaleMat * position;
// Offset model to correct location w.r.t instancing center
position = position + instance_position.xyz;
// Transform normal using rotation matrices
normal = gl_NormalMatrix * (RotMatH * RotMatP * RotMatR * gl_Normal);
}

View file

@ -0,0 +1,22 @@
// -*-C++-*-
#version 120
#extension GL_EXT_draw_instanced : enable
attribute vec3 instance_position; // (x,y,z)
attribute vec4 instance_rotation_and_scale; // (heading, pitch, roll, scale)
varying vec3 normal;
varying vec4 ecPosition;
void setupShadows(vec4 eyeSpacePos);
void render_ALS_base(in vec3 position);
void apply_instance_transforms(inout vec3 position, inout vec3 normal, in vec3 instance_position, in vec4 instance_rotation_and_scale);
void main()
{
vec3 position = gl_Vertex.xyz;
apply_instance_transforms(position, normal, instance_position, instance_rotation_and_scale);
render_ALS_base(position);
setupShadows(ecPosition);
}

View file

@ -0,0 +1,22 @@
// -*-C++-*-
#version 120
// Based on model-ALS-base.frag, with one-liner addition
// for mixing color with existing color.
varying vec4 instanceColor;
uniform sampler2D texture;
void render_model_ALS_base(in vec4 texel);
void main()
{
vec4 texel = texture2D(texture, gl_TexCoord[0].st);
// Mix instance color with default color
texel.rgb = (1 - instanceColor.a) * texel.rgb + instanceColor.a * instanceColor.rgb;
render_model_ALS_base(texel);
}

View file

@ -0,0 +1,30 @@
// -*-C++-*-
#version 120
#extension GL_EXT_draw_instanced : enable
// Based on object-instancing.vert, with minor additions for instance color
attribute vec3 instance_position; // (x,y,z)
attribute vec4 instance_rotation_and_scale; // (heading, pitch, roll, scale)
attribute vec4 instance_custom_attrib; // (r,g,b,a)
varying vec3 normal;
varying vec4 ecPosition;
varying vec4 instanceColor;
void setupShadows(vec4 eyeSpacePos);
void render_ALS_base(in vec3 position);
void apply_instance_transforms(inout vec3 position, inout vec3 normal, in vec3 instance_position, in vec4 instance_rotation_and_scale);
void main()
{
vec3 position = gl_Vertex.xyz;
apply_instance_transforms(position, normal, instance_position, instance_rotation_and_scale);
render_ALS_base(position);
setupShadows(ecPosition);
// Pass instance color to fragment shader from custom attribute
instanceColor = instance_custom_attrib;
}

View file

@ -0,0 +1,83 @@
// -*-C++-*-
// Based on default.frag, with one-liner addition
// for mixing color with existing color.
// Ambient term comes in gl_Color.rgb.
#version 120
varying vec4 diffuse_term;
varying vec3 normal;
varying vec4 ecPosition;
uniform sampler2D texture;
uniform sampler2D orthophotoTexture;
////fog "include" /////
uniform int fogType;
uniform bool orthophotoAvailable;
varying vec4 instanceColor;
vec3 fog_Func(vec3 color, int type);
//////////////////////
float getShadowing();
vec3 getClusteredLightsContribution(vec3 p, vec3 n, vec3 texel);
float luminance(vec3 color)
{
return dot(vec3(0.212671, 0.715160, 0.072169), color);
}
void main()
{
vec3 n;
float NdotL, NdotHV, fogFactor;
vec4 color = gl_Color;
vec3 lightDir = gl_LightSource[0].position.xyz;
vec3 halfVector = gl_LightSource[0].halfVector.xyz;
vec4 texel;
vec4 fragColor;
vec4 specular = vec4(0.0);
// If gl_Color.a == 0, this is a back-facing polygon and the
// normal should be reversed.
n = (2.0 * gl_Color.a - 1.0) * normal;
n = normalize(n);
NdotL = dot(n, lightDir);
if (NdotL > 0.0) {
float shadowmap = getShadowing();
color += diffuse_term * NdotL * shadowmap;
NdotHV = max(dot(n, halfVector), 0.0);
if (gl_FrontMaterial.shininess > 0.0)
specular.rgb = (gl_FrontMaterial.specular.rgb
* gl_LightSource[0].specular.rgb
* pow(NdotHV, gl_FrontMaterial.shininess)
* shadowmap);
}
color.a = diffuse_term.a;
// This shouldn't be necessary, but our lighting becomes very
// saturated. Clamping the color before modulating by the texture
// is closer to what the OpenGL fixed function pipeline does.
color = clamp(color, 0.0, 1.0);
texel = texture2D(texture, gl_TexCoord[0].st);
// Mix instance color with default color
texel.rgb = (1 - instanceColor.a) * texel.rgb + instanceColor.a * instanceColor.rgb;
if (orthophotoAvailable) {
vec4 sat_texel = texture2D(orthophotoTexture, gl_TexCoord[2].st);
if (sat_texel.a > 0) {
texel.rgb = sat_texel.rgb;
}
}
fragColor = color * texel + specular;
fragColor.rgb += getClusteredLightsContribution(ecPosition.xyz, n, texel.rgb);
fragColor.rgb = fog_Func(fragColor.rgb, fogType);
gl_FragColor = fragColor;
}

View file

@ -0,0 +1,68 @@
// -*-C++-*-
#version 120
#extension GL_EXT_draw_instanced : enable
// Based on object-instancing-lowspec.vert, with minor additions for instance color
#define MODE_OFF 0
#define MODE_DIFFUSE 1
#define MODE_AMBIENT_AND_DIFFUSE 2
attribute vec3 instance_position; // (x,y,z)
attribute vec4 instance_rotation_and_scale; // (heading, pitch, roll, scale)
attribute vec4 instance_custom_attrib; // (r,g,b,a)
varying vec4 diffuse_term;
varying vec3 normal;
varying vec4 ecPosition;
varying vec4 instanceColor;
uniform int colorMode;
void setupShadows(vec4 eyeSpacePos);
void apply_instance_transforms(inout vec3 position, inout vec3 normal, in vec3 instance_position, in vec4 instance_rotation_and_scale);
void main()
{
vec3 position = gl_Vertex.xyz;
apply_instance_transforms(position, normal, instance_position, instance_rotation_and_scale);
gl_Position = gl_ModelViewProjectionMatrix * vec4(position, gl_Vertex.w);
// Pass instance color to fragment shader from custom attribute
instanceColor = instance_custom_attrib;
/************* The following is copied from default.vert *************/
ecPosition = gl_ModelViewMatrix * gl_Vertex;
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
gl_TexCoord[2] = gl_TextureMatrix[2] * gl_MultiTexCoord2;
vec4 ambient_color, diffuse_color;
if (colorMode == MODE_DIFFUSE) {
diffuse_color = gl_Color;
ambient_color = gl_FrontMaterial.ambient;
} else if (colorMode == MODE_AMBIENT_AND_DIFFUSE) {
diffuse_color = gl_Color;
ambient_color = gl_Color;
} else {
diffuse_color = gl_FrontMaterial.diffuse;
ambient_color = gl_FrontMaterial.ambient;
}
diffuse_term = diffuse_color * gl_LightSource[0].diffuse;
vec4 constant_term = gl_FrontMaterial.emission + ambient_color *
(gl_LightModel.ambient + gl_LightSource[0].ambient);
// Super hack: if diffuse material alpha is less than 1, assume a
// transparency animation is at work
if (gl_FrontMaterial.diffuse.a < 1.0)
diffuse_term.a = gl_FrontMaterial.diffuse.a;
else
diffuse_term.a = gl_Color.a;
// Another hack for supporting two-sided lighting without using
// gl_FrontFacing in the fragment shader.
gl_FrontColor.rgb = constant_term.rgb; gl_FrontColor.a = 1.0;
gl_BackColor.rgb = constant_term.rgb; gl_BackColor.a = 0.0;
setupShadows(ecPosition);
}

View file

@ -0,0 +1,60 @@
// -*-C++-*-
#version 120
#extension GL_EXT_draw_instanced : enable
#define MODE_OFF 0
#define MODE_DIFFUSE 1
#define MODE_AMBIENT_AND_DIFFUSE 2
attribute vec3 instance_position; // (x,y,z)
attribute vec4 instance_rotation_and_scale; // (heading, pitch, roll, scale)
varying vec4 diffuse_term;
varying vec3 normal;
varying vec4 ecPosition;
uniform int colorMode;
void setupShadows(vec4 eyeSpacePos);
void apply_instance_transforms(inout vec3 position, inout vec3 normal, in vec3 instance_position, in vec4 instance_rotation_and_scale);
void main()
{
vec3 position = gl_Vertex.xyz;
apply_instance_transforms(position, normal, instance_position, instance_rotation_and_scale);
gl_Position = gl_ModelViewProjectionMatrix * vec4(position, gl_Vertex.w);
/************* The following is copied from default.vert *************/
ecPosition = gl_ModelViewMatrix * gl_Vertex;
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
gl_TexCoord[2] = gl_TextureMatrix[2] * gl_MultiTexCoord2;
vec4 ambient_color, diffuse_color;
if (colorMode == MODE_DIFFUSE) {
diffuse_color = gl_Color;
ambient_color = gl_FrontMaterial.ambient;
} else if (colorMode == MODE_AMBIENT_AND_DIFFUSE) {
diffuse_color = gl_Color;
ambient_color = gl_Color;
} else {
diffuse_color = gl_FrontMaterial.diffuse;
ambient_color = gl_FrontMaterial.ambient;
}
diffuse_term = diffuse_color * gl_LightSource[0].diffuse;
vec4 constant_term = gl_FrontMaterial.emission + ambient_color *
(gl_LightModel.ambient + gl_LightSource[0].ambient);
// Super hack: if diffuse material alpha is less than 1, assume a
// transparency animation is at work
if (gl_FrontMaterial.diffuse.a < 1.0)
diffuse_term.a = gl_FrontMaterial.diffuse.a;
else
diffuse_term.a = gl_Color.a;
// Another hack for supporting two-sided lighting without using
// gl_FrontFacing in the fragment shader.
gl_FrontColor.rgb = constant_term.rgb; gl_FrontColor.a = 1.0;
gl_BackColor.rgb = constant_term.rgb; gl_BackColor.a = 0.0;
setupShadows(ecPosition);
}