Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
Shader: Debris
|
||||
Vertex: data/shaders/source/pbr_debris_vs.txt
|
||||
Fragment: data/shaders/source/pbr_debris_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex wreckage = 0
|
||||
Variable: tex skybox = 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: float life = global LIFE
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
|
||||
Shader: AsteroidTonalite
|
||||
Vertex: data/shaders/source/asteroid_mine_vs.txt
|
||||
Fragment: data/shaders/source/asteroid_mine_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex normalMap = 0
|
||||
Variable: tex emissive = 1
|
||||
Variable: tex skybox = 2
|
||||
Variable: float mineBuild = state_vars Asteroid::HasBase
|
||||
Variable: vec3[2] colors = <1.0,1.0,1.0> <1.0,0.0,0.0>
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
|
||||
Shader: AsteroidMagnetite
|
||||
Vertex: data/shaders/source/asteroid_mine_vs.txt
|
||||
Fragment: data/shaders/source/asteroid_mine_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex normalMap = 0
|
||||
Variable: tex emissive = 1
|
||||
Variable: tex skybox = 2
|
||||
Variable: float mineBuild = state_vars Asteroid::HasBase
|
||||
Variable: vec3[2] colors = <0.8,0.43,0.3> <0.0,1.0,1.0>
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
|
||||
Shader: AsteroidPegmatite
|
||||
Vertex: data/shaders/source/asteroid_mine_vs.txt
|
||||
Fragment: data/shaders/source/asteroid_mine_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex normalMap = 0
|
||||
Variable: tex emissive = 1
|
||||
Variable: tex skybox = 2
|
||||
Variable: float mineBuild = state_vars Asteroid::HasBase
|
||||
Variable: vec3[2] colors = <0.8,0.66,0.47> <0.0,0.0,0.8>
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
|
||||
Shader: PBRShipFlag
|
||||
Vertex: data/shaders/source/pbr_common_flag_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_ship_flag_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex skybox = 5
|
||||
|
||||
Variable: int flagNumber = emp_flag
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: float acceleration = obj_acceleration
|
||||
Variable: float velocity = obj_velocity
|
||||
|
||||
// supports dont use transparency and don't generate shields or support flags
|
||||
Shader: PBRShipSupport
|
||||
Vertex: data/shaders/source/pbr_common_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_ship_support_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex skybox = 5
|
||||
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: float acceleration = obj_acceleration
|
||||
Variable: float velocity = obj_velocity
|
||||
|
||||
Shader: PBRStations
|
||||
Vertex: data/shaders/source/pbr_common_flag_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_stations_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex skybox = 5
|
||||
|
||||
Variable: int flagNumber = emp_flag
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
|
||||
Shader: PBRStations_Beacon
|
||||
Vertex: data/shaders/source/pbr_common_flag_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_stations_beacon_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex skybox = 5
|
||||
|
||||
Variable: int flagNumber = emp_flag
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
|
||||
Shader: PBRStations_FTLStorage
|
||||
Vertex: data/shaders/source/pbr_common_flag_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_stations_ftl_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex skybox = 5
|
||||
|
||||
Variable: int flagNumber = emp_flag
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
|
||||
Shader: PBRStations_Habitat
|
||||
Vertex: data/shaders/source/pbr_common_flag_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_stations_habitat_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: int flagNumber = emp_flag
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex biome = 5
|
||||
Variable: tex skybox = 6
|
||||
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
|
||||
|
||||
Shader: PBRStations_Forge
|
||||
Vertex: data/shaders/source/pbr_common_flag_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_stations_forge_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex biome = 5
|
||||
Variable: tex skybox = 6
|
||||
|
||||
Variable: int flagNumber = emp_flag
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
|
||||
Shader: PBRShipRemnant
|
||||
Vertex: data/shaders/source/pbr_common_remnant_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_ship_remnant_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex skybox = 5
|
||||
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: float acceleration = obj_acceleration
|
||||
Variable: float velocity = obj_velocity
|
||||
|
||||
Shader: PBRShipSeed
|
||||
Vertex: data/shaders/source/pbr_common_remnant_lodding_vs.txt
|
||||
Fragment: data/shaders/source/pbr_ship_seed_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: float time = game_time_cycle 64.0
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normals = 1
|
||||
Variable: tex masks = 2
|
||||
Variable: tex damaged = 3
|
||||
Variable: tex emissives = 4
|
||||
Variable: tex skybox = 5
|
||||
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 damage = obj_quadrant_damage
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: float acceleration = obj_acceleration
|
||||
Variable: float velocity = obj_velocity
|
||||
|
||||
Shader: PBR_obelisk
|
||||
Vertex: data/shaders/source/obelisk_vs.txt
|
||||
Fragment: data/shaders/source/obelisk_lodding_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex model = 0
|
||||
Variable: tex detail = 1
|
||||
Variable: tex skybox = 2
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
@@ -0,0 +1,141 @@
|
||||
Shader: FlatAtmosphere
|
||||
Vertex: data/shaders/source/atmosphere_vs.txt
|
||||
Fragment: data/shaders/source/atmosphere_flat_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec3[2] colors = <0.5,0.7,1.0> <1.0,0.5,0.1>
|
||||
Variable: float nodeScale = global MODEL_SCALE
|
||||
|
||||
Shader: TerranAtmosphere
|
||||
Vertex: data/shaders/source/atmosphere_vs.txt
|
||||
Fragment: data/shaders/source/atmosphere_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec3[2] colors = <0.5,0.7,1.0> <1.0,0.5,0.1>
|
||||
Variable: float nodeScale = node_scale
|
||||
|
||||
Shader: DustyAtmosphere
|
||||
Vertex: data/shaders/source/atmosphere_vs.txt
|
||||
Fragment: data/shaders/source/atmosphere_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec3[2] colors = <1.0,0.8,0.4> <1.0,0.5,0.1>
|
||||
Variable: float nodeScale = node_scale
|
||||
|
||||
Shader: CrystalAtmosphere
|
||||
Vertex: data/shaders/source/atmosphere_vs.txt
|
||||
Fragment: data/shaders/source/atmosphere_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec3[2] colors = <0.8,0.4,1.0> <1.0,0.1,0.4>
|
||||
Variable: float nodeScale = node_scale
|
||||
|
||||
Shader: VulcanicAtmosphere
|
||||
Vertex: data/shaders/source/atmosphere_vs.txt
|
||||
Fragment: data/shaders/source/atmosphere_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec3[2] colors = <1.0,0.6,0.4> <6.0,0.3,0.2>
|
||||
Variable: float nodeScale = node_scale
|
||||
|
||||
Shader: IceAtmosphere
|
||||
Vertex: data/shaders/source/atmosphere_vs.txt
|
||||
Fragment: data/shaders/source/atmosphere_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec3[2] colors = <0.6,0.8,1.0> <1.0,0.5,0.5>
|
||||
Variable: float nodeScale = node_scale
|
||||
|
||||
Shader: PlanetSurfaceGeneric
|
||||
Vertex: data/shaders/source/pl_generic_vs.txt
|
||||
Fragment: data/shaders/source/pl_generic_ps.txt
|
||||
|
||||
Variable: tex surfaceData = 0
|
||||
Variable: tex biomes = 1
|
||||
Variable: tex lookup = 2
|
||||
Variable: vec2 texSize = global PLANET_SURFACE_GRID_SIZE
|
||||
Variable: vec2 fullSize = global PLANET_FULL_GRID_SIZE
|
||||
Variable: vec2 cracksColorIntensity = <0.95,6.0>
|
||||
Variable: vec4[3] biomesPicks = global BIOME_PICKS
|
||||
|
||||
Shader: ProceduralPlanet
|
||||
Vertex: data/shaders/source/planet_procedural_clean_vs.txt
|
||||
Fragment: data/shaders/source/planet_procedural_clean_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex biomes = 0
|
||||
Variable: tex cities = 1
|
||||
Variable: tex differenceNoise = 2
|
||||
Variable: tex lookup = 3
|
||||
Variable: tex cityGlow = 4
|
||||
// Variable: tex testSplat = 5 // potential damage would go here
|
||||
Variable: tex skybox = 6
|
||||
Variable: tex surfaceData = 7
|
||||
Variable: vec2 texSize = global PLANET_SURFACE_GRID_SIZE
|
||||
Variable: vec2 fullSize = global PLANET_FULL_GRID_SIZE
|
||||
Variable: float time = game_time_cycle 8
|
||||
Variable: float population = state_vars Planet::Population //might be used later!
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec3 wsPos = node_position //not used
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: vec2 cracksColorIntensity = <0.95,6.0>
|
||||
Variable: vec4[3] biomesPicks = global BIOME_PICKS
|
||||
|
||||
Shader: ProceduralMoon
|
||||
Vertex: data/shaders/source/moon_procedural_vs.txt
|
||||
Fragment: data/shaders/source/moon_procedural_ps.txt
|
||||
|
||||
Variable: tex biomes = 0
|
||||
//Variable: tex cities = 1
|
||||
Variable: tex lookup = 2
|
||||
//Variable: tex cityGlow = 3
|
||||
Variable: tex skybox = 6
|
||||
// Variable: float population = state_vars Planet::Population //will be used later!
|
||||
Variable: float lightRadius = light_radius 0 1
|
||||
// Variable: vec3 wsPos = node_position //not used
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
//Variable: vec3 ownerColor = node_color
|
||||
Variable: vec4 cracksColorIntensityBiomeSeed = <0.95,6.0,0.23,0.0> // cracks color, make intensity 0 for cracks off, mono color lookup from biome, 0-1 random seed value for moon procedural splatmap.
|
||||
Variable: vec4 biomeOffsets = <0.75,0.0,0.25,0.5> // xy for which two surface textures to blend
|
||||
// Variable: float moonBaseBuilt = 1.0
|
||||
|
||||
Shader: RingworldSurface
|
||||
Vertex: data/shaders/source/ringworld_procedural_vs.txt
|
||||
Fragment: data/shaders/source/ringworld_procedural_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex biomes = 0
|
||||
Variable: tex cities = 1
|
||||
Variable: tex differenceNoise = 2
|
||||
Variable: tex lookup = 3
|
||||
Variable: tex cityGlow = 4
|
||||
Variable: tex skybox = 6
|
||||
Variable: tex surfaceData = 7
|
||||
Variable: vec2 texSize = global PLANET_SURFACE_GRID_SIZE
|
||||
Variable: vec2 fullSize = global PLANET_FULL_GRID_SIZE
|
||||
Variable: float population = state_vars Planet::Population
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
Variable: vec4 ownerColor = node_color
|
||||
// glow cracks are unsuported by ringworld shader!
|
||||
// Variable: vec2 cracksColorIntensity = <0.95,6.0>
|
||||
// uncomment for hardcoded terran
|
||||
// Variable: vec4[3] biomesPicks = <0.5,0.5,0.6,0.2> <0.75,0.5,0.61,0.1> <0.5,0.0,0.6,0.05>
|
||||
Variable: vec4[3] biomesPicks = global BIOME_PICKS
|
||||
Variable: float time = game_time
|
||||
Variable: float cycle = game_time_cycle = 30
|
||||
|
||||
Shader: CrystalSurface
|
||||
Vertex: data/shaders/source/pl_normal_vs.txt
|
||||
Fragment: data/shaders/source/pl_gradglow_ps.txt
|
||||
|
||||
Variable: tex diffuseTex = 0
|
||||
Variable: tex glowTex = 1
|
||||
Variable: tex normalRGBspecA = 2
|
||||
Variable: tex cities = 3
|
||||
Variable: tex diffNoise = 4
|
||||
Variable: float population = state_vars Planet::Population
|
||||
Variable: float[4] uvOffsets = game_time_cycle 14 11.4 10.7 11.23
|
||||
Variable: vec3[2] glowGradient = <1,0,0.5> <1,1,1>
|
||||
Variable: float flipState = unique
|
||||
@@ -0,0 +1,543 @@
|
||||
Shader: BaseTexture
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/base_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
Shader: Flat
|
||||
Vertex: data/shaders/source/base_mesh_uv_vs.txt
|
||||
Fragment: data/shaders/source/base_mesh_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
Shader: Skycube
|
||||
Vertex: data/shaders/source/skybox_vs.txt
|
||||
Fragment: data/shaders/source/skybox_detailed_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex detail = 0
|
||||
Variable: tex skybox = 1
|
||||
|
||||
Shader: ColoredFlat
|
||||
Vertex: data/shaders/source/base_mesh_uv_vs.txt
|
||||
Fragment: data/shaders/source/base_mesh_colored_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec4 color = node_color
|
||||
|
||||
Shader: Lighting
|
||||
Vertex: data/shaders/source/smooth_light_vs.txt
|
||||
Fragment: data/shaders/source/smooth_light_ps.txt
|
||||
|
||||
Variable: tex diffuseRGBspecA = 0
|
||||
|
||||
Shader: NormalLighting
|
||||
Vertex: data/shaders/source/smooth_light_normal_vs.txt
|
||||
Fragment: data/shaders/source/smooth_light_normal_ps.txt
|
||||
|
||||
Variable: tex diffuseRGB = 0
|
||||
Variable: tex normalMap = 1
|
||||
|
||||
Shader: GlowLighting
|
||||
Vertex: data/shaders/source/smooth_light_vs.txt
|
||||
Fragment: data/shaders/source/smooth_light_glow_ps.txt
|
||||
|
||||
Variable: tex diffuseRGBglowA = 0
|
||||
|
||||
Shader: GlowNormalLighting
|
||||
Vertex: data/shaders/source/smooth_light_normal_vs.txt
|
||||
Fragment: data/shaders/source/smooth_light_normal_glow_ps.txt
|
||||
|
||||
Variable: tex diffuseRGBglowA = 0
|
||||
Variable: tex normalMap = 1
|
||||
|
||||
Shader: SupportShip
|
||||
Vertex: data/shaders/source/support_ship_vs.txt
|
||||
Fragment: data/shaders/source/support_ship_ps.txt
|
||||
|
||||
Variable: tex diffuseRGB = 0
|
||||
Variable: tex normalMap = 1
|
||||
Variable: tex specularMap = 2
|
||||
Variable: tex glossMap = 3
|
||||
Variable: tex skybox = 4
|
||||
Variable: tex masks = 5
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: vec3 glowColor = <1.0,0.9,0.1>
|
||||
Variable: float thrust = 1.0
|
||||
Variable: mat3 invView = inverse_view
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
|
||||
Shader: PBRShip
|
||||
Vertex: data/shaders/source/pbr_ship_vs.txt
|
||||
Fragment: data/shaders/source/pbr_ship_ps.txt
|
||||
|
||||
Variable: tex diffuse = 0
|
||||
Variable: tex normalAOlights = 1
|
||||
Variable: tex shield = 2
|
||||
Variable: tex masks = 3
|
||||
Variable: tex skybox = 4
|
||||
Variable: vec4 ownerColor = node_color
|
||||
Variable: vec3 glowColor = <1.0,0.9,0.1>
|
||||
Variable: float thrust = 1.0
|
||||
Variable: float camDist = node_distance
|
||||
Variable: float nodeScale = node_scale
|
||||
|
||||
Shader: GhoooostShip
|
||||
Vertex: data/shaders/source/support_ship_vs.txt
|
||||
Fragment: data/shaders/source/ghoooost_ship_ps.txt
|
||||
|
||||
Variable: tex normalMap = 0
|
||||
|
||||
//Renders a ship model as if it were a blueprint
|
||||
Shader: Blueprint
|
||||
Vertex: data/shaders/source/blueprint_vs.txt
|
||||
Fragment: data/shaders/source/blueprint_ps.txt
|
||||
|
||||
Variable: tex diffuse = 0
|
||||
|
||||
Shader: PlanetRing
|
||||
Vertex: data/shaders/source/planet_ring_vs.txt
|
||||
Fragment: data/shaders/source/planet_ring_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: tex hardNoise = 1
|
||||
Variable: vec2 starDir = global STAR_DIRECTION
|
||||
Variable: float plSize = global PLANET_RING_RATIO
|
||||
Variable: float ringMin = global RING_MIN
|
||||
Variable: float ringMax = global RING_MAX
|
||||
|
||||
Shader: AccretionDisk
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/accretion_disk_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float time = game_time_cycle 240
|
||||
|
||||
Shader: Nova
|
||||
Vertex: data/shaders/source/nova_vs.txt
|
||||
Fragment: data/shaders/source/nova_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float age = global NOVA_AGE
|
||||
//Variable: float time = game_time_cycle 15
|
||||
|
||||
Shader: StarSurface
|
||||
Vertex: data/shaders/source/star_anim_vs.txt
|
||||
Fragment: data/shaders/source/star_anim_high_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex[2] texture = 0 1
|
||||
Variable: float[5] cycles = game_time_cycle 120 152 109.6 194.72 117.68
|
||||
Variable: float temperature = state_vars Star::temperature
|
||||
Variable: float nodeScale = node_scale
|
||||
Variable: vec4 wsRot = node_rotation
|
||||
|
||||
Shader: PopupStarSurface
|
||||
Vertex: data/shaders/source/star_anim_gui_vs.txt
|
||||
Fragment: data/shaders/source/star_anim_gui_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex[2] texture = 0 1
|
||||
Variable: float[5] cycles = game_time_cycle 15 19 13.7 24.34 14.71
|
||||
Variable: float temperature = global STAR_TEMP
|
||||
|
||||
Shader: Corona
|
||||
Vertex: data/shaders/source/corona_vs.txt
|
||||
Fragment: data/shaders/source/corona_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec4 color = node_color
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
Variable: float time = game_time_cycle 240
|
||||
Variable: float temperature = global STAR_TEMP
|
||||
Variable: float scale = node_scale
|
||||
|
||||
Shader: Prominence
|
||||
Vertex: data/shaders/source/prominence_vs.txt
|
||||
Fragment: data/shaders/source/prominence_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec4[8] curve = global BEZIER_POINTS
|
||||
|
||||
Shader: ColoredDisc
|
||||
Vertex: data/shaders/source/disc_vs.txt
|
||||
Fragment: data/shaders/source/disc_ps.txt
|
||||
|
||||
Shader: ColorFlicker
|
||||
Vertex: data/shaders/source/flicker_vs.txt
|
||||
Fragment: data/shaders/source/base_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float[2] time = game_time_cycle 0.025 0.031
|
||||
Variable: float tOffset = unique
|
||||
|
||||
Shader: NodeColorFlicker
|
||||
Vertex: data/shaders/source/flicker_node_color_vs.txt
|
||||
Fragment: data/shaders/source/base_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float[2] time = game_time_cycle 0.025 0.031
|
||||
Variable: float tOffset = unique
|
||||
Variable: vec4 color = node_color
|
||||
|
||||
Shader: MaskColored
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/mask_colored_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: tex mask = 1
|
||||
|
||||
Shader: NodeColored
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/colored_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec4 color = node_color
|
||||
|
||||
Shader: NodeColoredSelectable
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/colored_selectable_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float selected = node_selected
|
||||
|
||||
Shader: ShipGroup
|
||||
Vertex: data/shaders/source/approach_uv_vs.txt
|
||||
Fragment: data/shaders/source/ship_group_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec4 color = node_color
|
||||
Variable: float selected = node_selected
|
||||
Variable: vec4 glowColor = global GLOW_COLOR
|
||||
Variable: float cycle = time_cycle 1.25
|
||||
Variable: float approach = global APPROACH
|
||||
|
||||
Shader: ColoredAdd
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/colored_add_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
Shader: SignedAdd
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/signed_add_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
Shader: SignedInverted
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/signed_inverted_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
Shader: BeamFade
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/beam_fade_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float cycle = game_time_cycle 2.0
|
||||
Variable: float offset = unique
|
||||
|
||||
Shader: Tractor
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/tractor_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: tex noise = 1
|
||||
Variable: float cycle = game_time_cycle 8.0
|
||||
|
||||
Shader: ColoredAddPlain
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/colored_add_plain_ps.txt
|
||||
|
||||
Shader: Tiled
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/tiled_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
#if fallback
|
||||
Shader: GuiSkin
|
||||
Vertex: data/shaders/source/gui_skin_vs.txt
|
||||
Fragment: data/shaders/source/gui_skin_simple_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec2 texSize = tex_size 0
|
||||
Variable: vec4 margin_src = skin_margin_src
|
||||
Variable: vec4 margin_dest = skin_margin_dest
|
||||
Variable: vec2 pos = skin_src_pos
|
||||
Variable: vec2 size = skin_src_size
|
||||
Variable: vec2 size_out = skin_dst_size
|
||||
Variable: vec2 dim_mode = skin_dim_modes
|
||||
Variable: float gradientCount = skin_grd_count
|
||||
Variable: float gradientMode = skin_grd_mode
|
||||
Variable: vec4 gradientRect = skin_grd_rects
|
||||
Variable: vec4[4] gradientColors = skin_grd_colors
|
||||
#else
|
||||
Shader: GuiSkin
|
||||
Vertex: data/shaders/source/gui_skin_vs.txt
|
||||
Fragment: data/shaders/source/gui_skin_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec2 texSize = tex_size 0
|
||||
Variable: vec4 margin_src = skin_margin_src
|
||||
Variable: vec4 margin_dest = skin_margin_dest
|
||||
Variable: vec2 pos = skin_src_pos
|
||||
Variable: vec2 size = skin_src_size
|
||||
Variable: vec2 size_out = skin_dst_size
|
||||
Variable: vec2 dim_mode = skin_dim_modes
|
||||
Variable: float gradientCount = skin_grd_count
|
||||
Variable: float gradientMode = skin_grd_mode
|
||||
Variable: vec4[8] gradientRects = skin_grd_rects
|
||||
Variable: vec4[32] gradientColors = skin_grd_colors
|
||||
#endif
|
||||
|
||||
Shader: HSVPalette
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/hsv_palette_ps.txt
|
||||
|
||||
Variable: float value = global HSV_VALUE
|
||||
Variable: float minSat = global HSV_SAT_START
|
||||
Variable: float maxSat = global HSV_SAT_END
|
||||
|
||||
Shader: Fullscreen
|
||||
Vertex: data/shaders/source/fullscreen_vs.txt
|
||||
Fragment: data/shaders/source/fs_plain_ps.txt
|
||||
|
||||
Variable: tex screen = 0
|
||||
|
||||
Shader: FullscreenPostProcess
|
||||
Vertex: data/shaders/source/fs_post_process_vs.txt
|
||||
Fragment: data/shaders/source/fs_post_process_ps.txt
|
||||
Settings Reload: True
|
||||
|
||||
Variable: tex screen = 0
|
||||
Variable: tex depthTex = 7
|
||||
Variable: vec2 texSize = tex_size 0
|
||||
Variable: vec2[2] lightPos = light_screen_position 0 1
|
||||
Variable: float[2] lightActive = light_active 0 1
|
||||
Variable: float cycle = game_time_cycle 240.0
|
||||
Variable: float[2] lightRadius = light_radius 0 1
|
||||
|
||||
Shader: MenuRender
|
||||
Vertex: data/shaders/source/fullscreen_vs.txt
|
||||
Fragment: data/shaders/source/fs_menu_ps.txt
|
||||
|
||||
Variable: tex screen = 0
|
||||
|
||||
Shader: MenuBlur
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/fs_menu_ps.txt
|
||||
|
||||
Variable: tex screen = 0
|
||||
|
||||
Shader: MenuSaveBackground
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/fs_menu_save_ps.txt
|
||||
|
||||
Variable: tex screen = 0
|
||||
|
||||
Shader: TradeLine
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/trade_line_ps.txt
|
||||
|
||||
Variable: float time = game_time_cycle 15
|
||||
|
||||
Shader: Hex
|
||||
Vertex: data/shaders/source/hex_vs.txt
|
||||
Fragment: data/shaders/source/hex_ps.txt
|
||||
|
||||
Shader: CutOff
|
||||
Vertex: data/shaders/source/cutoff_vs.txt
|
||||
Fragment: data/shaders/source/cutoff_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float cutoff = global CUTOFF_PCT
|
||||
|
||||
Shader: WireframeCutOff
|
||||
Vertex: data/shaders/source/cutoff_vs.txt
|
||||
Fragment: data/shaders/source/wireframe_cutoff_ps.txt
|
||||
|
||||
Variable: float cutoff = global CUTOFF_PCT
|
||||
|
||||
Shader: Desaturate
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/desaturate_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float level = global SATURATION_LEVEL
|
||||
|
||||
Shader: RadialProgress
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/radial_progress_ps.txt
|
||||
|
||||
Variable: float progress = global PROGRESS
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
|
||||
Shader: RadialDimmed
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/radial_dim_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float progress = global PROGRESS
|
||||
Variable: float dim_factor = global DIM_FACTOR
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
|
||||
Shader: Circle
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/circle_ps.txt
|
||||
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
|
||||
Shader: OrbitCircle
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/orbit_circle_ps.txt
|
||||
|
||||
Variable: float circle_min = global CIRCLE_MIN
|
||||
Variable: float circle_max = global CIRCLE_MAX
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
Variable: vec4 captureColor = global CAPTURE_COLOR
|
||||
Variable: float capturePct = global CAPTURE_PROGRESS
|
||||
|
||||
Shader: FleetCircle
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/fleet_circle_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
|
||||
Shader: RangeCircle
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/range_circle_ps.txt
|
||||
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
Variable: float dist = node_distance
|
||||
Variable: float scale = node_scale
|
||||
Variable: float minRad = plane_minrad
|
||||
Variable: float maxRad = plane_maxrad
|
||||
|
||||
Shader: FireArc2D
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/fire_arc_ps.txt
|
||||
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
Variable: float minRad = global MIN_RAD
|
||||
Variable: float maxRad = global MAX_RAD
|
||||
|
||||
Shader: Ping
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/ping_ps.txt
|
||||
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
|
||||
Shader: SystemPlane
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/system_plane_ps.txt
|
||||
|
||||
Variable: float planeDist = global PLANE_DISTANCE
|
||||
Variable: float radius = global RADIUS
|
||||
Variable: float inner_radius = global INNER_RADIUS
|
||||
Variable: vec4 glowColor = global GLOW_COLOR
|
||||
Variable: float cycle = time_cycle 2.5
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
|
||||
Shader: GalaxyPlane
|
||||
Vertex: data/shaders/source/radial_uv_vs.txt
|
||||
Fragment: data/shaders/source/galaxy_plane_ps.txt
|
||||
|
||||
Variable: float planeDist = global PLANE_DISTANCE
|
||||
Variable: float radius = global RADIUS
|
||||
Variable: vec4 sprite_pos = sprite_pos
|
||||
|
||||
Shader: Territory
|
||||
Vertex: data/shaders/source/territory_vs.txt
|
||||
Fragment: data/shaders/source/territory_ps.txt
|
||||
|
||||
Variable: vec4 stip_color = global STIPPLE_COLOR
|
||||
|
||||
Shader: DistantIcon
|
||||
Vertex: data/shaders/source/approach_uv_vs.txt
|
||||
Fragment: data/shaders/source/base_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float approach = global APPROACH
|
||||
|
||||
Shader: DistantIconLayered
|
||||
Vertex: data/shaders/source/distant_icon_vs.txt
|
||||
Fragment: data/shaders/source/distant_icon_ps.txt
|
||||
|
||||
Variable: tex[3] textures = 0, 1, 2
|
||||
Variable: float approach = global APPROACH
|
||||
Variable: float fade = global DISTANT_SPRITE_FADE
|
||||
Variable: float innerScale1 = global DISTANT_SPRITE_SCALE1
|
||||
Variable: vec4 sprite1 = global DISTANT_SPRITE1
|
||||
Variable: float innerScale2 = global DISTANT_SPRITE_SCALE2
|
||||
Variable: vec4 sprite2 = global DISTANT_SPRITE2
|
||||
|
||||
Shader: DistantPlanetIcon
|
||||
Vertex: data/shaders/source/distant_icon_vs.txt
|
||||
Fragment: data/shaders/source/distant_planet_icon_ps.txt
|
||||
|
||||
Variable: tex[4] textures = 0, 1, 2, 3
|
||||
Variable: float approach = global APPROACH
|
||||
Variable: float fade = global DISTANT_SPRITE_FADE
|
||||
Variable: float innerScale1 = global DISTANT_SPRITE_SCALE1
|
||||
Variable: vec4 sprite1 = global DISTANT_SPRITE1
|
||||
Variable: float innerScale2 = global DISTANT_SPRITE_SCALE2
|
||||
Variable: vec4 sprite2 = global DISTANT_SPRITE2
|
||||
Variable: float isDisabled = global IS_DISABLED
|
||||
Variable: float isUsed = global IS_USED
|
||||
Variable: float isExported = global IS_EXPORTED
|
||||
Variable: float isColonizing = global IS_COLONIZING
|
||||
Variable: float isOwned = global IS_OWNED
|
||||
Variable: float isDecaying = global IS_DECAYING
|
||||
Variable: float classIcon = global RESOURCE_CLASS
|
||||
Variable: vec4 captureColor = global CAPTURE_COLOR
|
||||
Variable: float capturePct = global CAPTURE_PROGRESS
|
||||
Variable: vec4 disableColor = global DISABLE_COLOR
|
||||
|
||||
Shader: DistantFadedIcon
|
||||
Vertex: data/shaders/source/distant_faded_icon_vs.txt
|
||||
Fragment: data/shaders/source/distant_faded_icon_ps.txt
|
||||
|
||||
Shader: MoveBeam
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/movebeam_ps.txt
|
||||
|
||||
Shader: Shield
|
||||
Vertex: data/shaders/source/shield_vs.txt
|
||||
Fragment: data/shaders/source/shield_ps.txt
|
||||
Variable: float strength = global SHIELD_STRENGTH
|
||||
|
||||
Shader: ShipIcon
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/ship_icon_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
Shader: ShipDistantIcon
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/ship_distant_icon_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float selected = node_selected
|
||||
|
||||
Shader: ResearchBeam
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/research_beam_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
Shader: ParticleLance
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/particle_lance_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
|
||||
Shader: InterpolateColor
|
||||
Vertex: data/shaders/source/base_uv_vs.txt
|
||||
Fragment: data/shaders/source/interpolate_ps.txt
|
||||
|
||||
Variable: tex texture = 0
|
||||
Variable: float factor = global FACTOR
|
||||
@@ -0,0 +1,92 @@
|
||||
uniform sampler2D texture;
|
||||
uniform float time;
|
||||
|
||||
varying vec2 uv;
|
||||
|
||||
//Percent from the center to the edge where the dust should stop
|
||||
const float innerRad = 0.05;
|
||||
const float outerFade = 0.8;
|
||||
|
||||
//How many different speeds the dust should move at
|
||||
const float steps = 36.0;
|
||||
//How fast it spins overall
|
||||
const float dustSpeed = 0.008;
|
||||
//Rate of speed increase, should be an integer
|
||||
const float dustSpeedCurve = 3.0;
|
||||
|
||||
//Fixed curve in the dust, should be an integer
|
||||
const float fixedSpiral = 3.0;
|
||||
|
||||
//Texture mapping settings to avoid peroidic patterns
|
||||
const float texRepeat = 6.0;
|
||||
const float stepOffset = 0.2;
|
||||
|
||||
//Temperature stepping as dust approaches the horizon
|
||||
const float kPerStep = 0.25;
|
||||
const float tempCurve = 3.35;
|
||||
|
||||
//Temp above which the light is artificially brightened
|
||||
const float hotTemp = 13000.0;
|
||||
//Temp below which the light is artificially darkened
|
||||
const float coolTemp = 2200.0;
|
||||
|
||||
const float twopi = 6.28318530718;
|
||||
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
if(temp < 6600.0)
|
||||
c.g = mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0));
|
||||
else
|
||||
c.g = mix(0.976, 0.75, mixRange(temp, 6600.0, 29800.0));
|
||||
if(temp < coolTemp)
|
||||
c *= temp / coolTemp;
|
||||
else if(temp > hotTemp)
|
||||
c *= (temp / hotTemp);
|
||||
return c;
|
||||
}
|
||||
|
||||
//Blends a number of rotating sections together
|
||||
//Sections rotate at integer multiples of the slowest speed to avoid animation hitching
|
||||
void main() {
|
||||
vec2 p = (uv - vec2(0.5)) * 2.0;
|
||||
float d = length(p);
|
||||
if(d > 1.0 || d <= innerRad)
|
||||
discard;
|
||||
d = 1.0 - ((d - innerRad) / (1.0 - innerRad));
|
||||
float a = atan(p.y,p.x);
|
||||
|
||||
//Whatever curve the second smoothstep makes, it works
|
||||
gl_FragColor.a = smoothstep(0.0,outerFade,d) * smoothstep(d,1.0,0.995);
|
||||
|
||||
float step = d * steps + 0.5;
|
||||
|
||||
float lowStep = floor(step);
|
||||
float hiStep = ceil(step);
|
||||
float lowPct = (hiStep - step);
|
||||
float hiPct = 1.0 - lowPct;
|
||||
|
||||
vec3 col = vec3(0.0);
|
||||
vec3 bbCol = blackBody(kPerStep * pow(step, tempCurve));
|
||||
|
||||
{
|
||||
vec2 st = vec2((a / twopi) - time * pow(lowStep, dustSpeedCurve) * dustSpeed + lowStep * stepOffset, (d - time * 4.0) * texRepeat);
|
||||
|
||||
st.y -= fixedSpiral * (a/twopi);
|
||||
col += bbCol * (texture2D(texture, st).r * lowPct);
|
||||
}
|
||||
|
||||
{
|
||||
vec2 st = vec2((a / twopi) - time * pow(hiStep, dustSpeedCurve) * dustSpeed + hiStep * stepOffset, (d - time * 4.0) * texRepeat);
|
||||
|
||||
st.y -= fixedSpiral * (a/twopi);
|
||||
col += bbCol * (texture2D(texture, st).r * hiPct);
|
||||
}
|
||||
|
||||
gl_FragColor.rgb = col;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
varying vec2 uv;
|
||||
uniform float approach;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
|
||||
vec4 cpos = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
cpos.z -= approach;
|
||||
gl_Position = cpos;
|
||||
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
#version 120
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool sss = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
const float pi = 3.14159265358;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 2.0;
|
||||
|
||||
|
||||
uniform sampler2D normalMap, emissive;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec3 colors[nLightCount];
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform vec4 ownerColor;
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.7 + 0.3;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.7 + 0.3;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.7 + 0.3;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, vertMask;
|
||||
varying vec2 uv;
|
||||
varying vec4 pos;
|
||||
|
||||
uniform float mineBuild;
|
||||
|
||||
varying vec3 light[nLightCount];
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0035);
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec4 square(vec4 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
float deriveZ(vec2 n) {
|
||||
return sqrt(abs(1.0 - n.x * n.x - n.y * n.y));
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p) {
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p) {
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec2 duv1Calc(vec2 uv) {
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec2 duv2Calc(vec2 uv) {
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
if((1.0 - mineBuild) + (1.0 - vertMask.g) < 1.0)
|
||||
discard;
|
||||
|
||||
vec2 uvP = uv;
|
||||
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
vec3 r = n;
|
||||
float NdotV = max(0.0, dot(n, v));
|
||||
vec3 albedo = vec3(0.0); // pure color of a surface
|
||||
vec3 substance = vec3(0.0); // essentially an rgb specular color extracted from the albedo through metalness
|
||||
float metalness = 0.0; // dielectric or metallic surface
|
||||
float orgRoughness = 0.0; // specular/reflection sharpness
|
||||
float cavity = 0.5; // hard multiplier
|
||||
float aoDetail = 1.0; // detail occluder for lights
|
||||
float aoModel = 1.0; // large scale usually pr model baked occluder
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// results
|
||||
vec3 color = vec3(0.0);
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
|
||||
if (normalMapping){
|
||||
// Normal and tangent setup
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec2 duv1 = duv1Calc(uvP);
|
||||
vec2 duv2 = duv2Calc(uvP);
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
if(parallax){
|
||||
|
||||
float p = (texture2D(normalMap, uvP).a * scaleBias.r - scaleBias.g);
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//inputs
|
||||
vec4 texSamp2 = texture2D(normalMap, uvP);
|
||||
vec4 texSamp3 = vec4(vec3(0.0), 1.0);
|
||||
|
||||
if (selfIllumination){
|
||||
texSamp3 = square(texture2D(emissive, clamp(uv.xy * vec2(0.5, 1.0), vec2(0.0125, 0.025), vec2(0.4875, 0.975))));
|
||||
texSamp3 = mix(vec4(0.0,0.0,0.0,1.0), texSamp3, vertMask.r * mineBuild);
|
||||
texSamp3.rgb *= 8.0; // way to faint texture bake correction
|
||||
}
|
||||
orgRoughness = square(texSamp2.a * 0.33 + 0.66);
|
||||
aoDetail = min(1.0, texSamp2.b + 0.5);
|
||||
albedo = vec3((texSamp2.b + texSamp2.a) * 0.5);
|
||||
albedo = toLinear(min(vec3(1.0), (mix(colors[0], colors[1], albedo) * 0.25 + 0.25)) + texSamp2.b * 0.25);
|
||||
|
||||
if (normalMapping){
|
||||
//calculate blue channel from x and y of normal map
|
||||
texSamp2.xy *= 2.0;
|
||||
texSamp2.xy -= 1.0;
|
||||
texSamp2.xy = texSamp2.yx;
|
||||
|
||||
vec3 normMap = normalize(vec3(texSamp2.xy, deriveZ(texSamp2.xy)));
|
||||
|
||||
n = normalize(TBN * normMap);
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
r = normalize(reflect(-v, n));
|
||||
|
||||
metalness = (1.0 - (orgRoughness * texSamp2.b)) * 0.25;
|
||||
substance = (0.04 - 0.04 * metalness) + albedo * metalness;
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
lights = texSamp3.r * colorLightsPrimary;
|
||||
lights += texSamp3.g * colorLightsSecondary;
|
||||
lights += texSamp3.b * colorLightsWindows;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel * aoDetail;
|
||||
}
|
||||
aoModel = max(0.25, (1.0 - ((1.0 - texSamp3.a) * mineBuild)) * aoDetail);
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (texSamp2.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (texSamp2.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
color += (albedo * (texSamp2.b * 0.5) + pow(S * HdotN, vec3(orgRoughness + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
color *= aoModel;
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0)));
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec4 in_color;
|
||||
attribute vec2 in_uv;
|
||||
uniform vec4 wsRot;
|
||||
|
||||
varying vec3 npos, vertMask;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec4 pos;
|
||||
|
||||
vec3 wsAllign(vec3 x){
|
||||
return x + 2.0 * cross(wsRot.xyz, cross(wsRot.xyz, x) + wsRot.w * x);
|
||||
}
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
pos = gl_ModelViewMatrix * in_vertex;
|
||||
|
||||
// convert view, normal and light vectors to world space and quaternion correct for model rotation
|
||||
mat3 tcamrot = transpose(mat3x3(gl_ModelViewMatrix));
|
||||
npos = (wsAllign(normalize(tcamrot * -pos.xyz)));
|
||||
|
||||
// special view vector to correct just for cubemap reflections
|
||||
normal = (tcamrot * (gl_NormalMatrix * wsAllign(normalize(in_normal))));
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = wsAllign(normalize((tcamrot * (((gl_LightSource[i].position)).xyz - pos.xyz))));
|
||||
}
|
||||
|
||||
vertMask = in_color.rgb;
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
const int nLightCount = 2;
|
||||
|
||||
uniform sampler2D diffuseRGB, normalMap;
|
||||
uniform vec3 colors[2];
|
||||
uniform float gloss;
|
||||
|
||||
varying vec3 normal, binormal, tangent;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
void main() {
|
||||
vec3 color = gl_FrontMaterial.diffuse.rgb;
|
||||
vec2 samp = texture2D(diffuseRGB, uv.xy).rg;
|
||||
vec3 texSamp = mix(colors[0], colors[1], samp.g) * samp.r;
|
||||
vec3 matspec = gl_FrontMaterial.specular.rgb;
|
||||
float shininess = mix(gl_FrontMaterial.shininess, gloss, samp.g);
|
||||
|
||||
vec3 normMap = (texture2D(normalMap, uv.xy).xyz * 2.0) - vec3(1.0);
|
||||
float ao = length(normMap) - 0.5;
|
||||
|
||||
vec3 n = normalize(normal) * normMap.z;
|
||||
n += normalize(binormal) * normMap.x;
|
||||
n += normalize(tangent) * normMap.y;
|
||||
n = normalize(n);
|
||||
|
||||
vec3 v = normalize(npos);
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
|
||||
vec3 diffuse = gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb * (0.5 + ao);
|
||||
|
||||
vec3 specular = vec3(0.0);
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float intensity = max(0.0, dot(n, light[i])) * falloff;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
vec3 r = -reflect(light[i], n);
|
||||
specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float intensity = max(0.0, dot(n, light[i])) * falloff;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
vec3 r = -reflect(light[i], n);
|
||||
specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
|
||||
}
|
||||
gl_FragColor.rgb = (diffuse * color * texSamp.rgb) + (specular * matspec);
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
#version 120
|
||||
|
||||
uniform sampler2D texture;
|
||||
|
||||
uniform float nodeScale;
|
||||
uniform vec3 colors[2];
|
||||
|
||||
varying vec2 uv;
|
||||
varying vec3 normal, binormal, tangent, light, light2;
|
||||
varying vec3 pos, center;
|
||||
varying float dist, dist2;
|
||||
|
||||
//planetRim is the (normal dot view) value corresponding to the edge of the planet on the atmosphere's model
|
||||
const float planetRim = 0.215;
|
||||
const float innerFade = 0.4;
|
||||
const float innerFadeMax = 0.9;
|
||||
const float atmosExaggerateDist = 250.0;
|
||||
const float outerFade = 0.1;
|
||||
|
||||
//The thickness of atmosphere that must be present before any haze is visible
|
||||
const float hazeThreshold = 0.03;
|
||||
//The brightness multiplier of the haze
|
||||
const float hazeBrightFactor = 7.0;
|
||||
//Fraction of haze that occurs beneath the clouds
|
||||
const float lowHaze = 0.7;
|
||||
const float hiHaze = 1.0 - lowHaze;
|
||||
|
||||
//How much cover is 100% cloud cover
|
||||
const float shadowDarkness = 0.75;
|
||||
|
||||
const float shadowDist = 0.025;
|
||||
|
||||
vec3 hazeColor(float NdL, vec3 lightCol) {
|
||||
float depth = pow(max(0.8 - NdL, 0.0), 2.0);
|
||||
float lum = max(lightCol.r, max(lightCol.g, lightCol.b));
|
||||
vec3 col = mix(colors[0], colors[1], depth);
|
||||
col /= max(col.r, max(col.g, col.b));
|
||||
return col * lum * pow(max((0.8 + NdL) * 0.5, 0.0), 1.3);
|
||||
}
|
||||
|
||||
vec4 alphaBlend(vec4 dest, vec4 src) {
|
||||
float alpha = src.a + (dest.a * (1.0 - src.a));
|
||||
return vec4( ((src.rgb * src.a) + (dest.rgb * dest.a * (1.0 - src.a))) / alpha, alpha);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 normMap = texture2D(texture, uv).rgb - vec3(0.5);
|
||||
float opacity = max((length(normMap) - 0.25) / 0.25, 0.0);
|
||||
vec3 n = normalize(normal);
|
||||
|
||||
vec3 cloudNorm = n * normMap.z;
|
||||
cloudNorm += normalize(binormal) * normMap.x;
|
||||
cloudNorm += normalize(tangent) * normMap.y;
|
||||
cloudNorm = normalize(cloudNorm);
|
||||
|
||||
float hazeBrightness = hazeBrightFactor / nodeScale;
|
||||
|
||||
vec3 p = -pos;
|
||||
//Apply a correction to the position to correct for the vertex structure
|
||||
p += nodeScale * (length(normal) - 1.0) * n;
|
||||
|
||||
vec3 v = normalize(p);
|
||||
vec3 c = normalize(center);
|
||||
float cosTheta = -dot(c,v);
|
||||
float theta = acos(cosTheta);
|
||||
|
||||
float dToCenter = length(center);
|
||||
float dToAtmos = length(p);
|
||||
float dToFarAtmos;
|
||||
float dToSurface;
|
||||
{
|
||||
float k = sin(theta) * dToCenter;
|
||||
float e = sqrt(nodeScale*nodeScale - k*k);
|
||||
dToSurface = sqrt(dToCenter*dToCenter - k*k) - e;
|
||||
|
||||
e = sqrt(pow(nodeScale*1.015,2.0) - k*k);
|
||||
dToFarAtmos = dToAtmos + e*2.0;
|
||||
}
|
||||
|
||||
vec3 l = normalize(light);
|
||||
float ndv = n.z;
|
||||
if(ndv < 0.0)
|
||||
discard;
|
||||
float ndl = dot(n,l);
|
||||
|
||||
//Calculate shadow cover
|
||||
float shadow = 0.0;
|
||||
if(ndl > 0.0 && ndv > planetRim) {
|
||||
vec2 off;
|
||||
off.x = asin(dot(binormal,l)) * shadowDist / 6.283;
|
||||
off.y = asin(dot(tangent,l)) * shadowDist / 6.283;
|
||||
|
||||
float thickness = max((length(texture2D(texture, uv + off.xy).rgb - vec3(0.5))-0.25)/0.25, 0.0) * sqrt(ndl);
|
||||
shadow = thickness * shadowDarkness;
|
||||
}
|
||||
|
||||
float lightFactor = 1.0 / (1.0 + (gl_LightSource[0].quadraticAttenuation * dist * dist));
|
||||
vec3 r = normalize(-reflect(l, n));
|
||||
float rDv = dot(r,v);
|
||||
|
||||
float nDl = dot(cloudNorm,light);
|
||||
float nl = max(0.0, nDl);
|
||||
|
||||
//Calculate atmospheric haze
|
||||
//Haze increases in brightness with depth through the atmosphere
|
||||
//The color changes as light is preferentially scattered away by frequency
|
||||
|
||||
vec3 haze = vec3(0.0);
|
||||
//if(ndv >= planetRim) {
|
||||
// float hazeFactor = max(dToSurface - dToAtmos - hazeThreshold, 0.0) * hazeBrightness;
|
||||
// haze = hazeColor(ndl, gl_LightSource[0].diffuse.rgb * lightFactor) * hazeFactor * 0.0;
|
||||
//}
|
||||
float hazeLum = max(haze.r, max(haze.g, haze.b));
|
||||
|
||||
vec3 rimHaze = vec3(0.0);
|
||||
float inFade = innerFade;
|
||||
if(ndv <= inFade) {
|
||||
float hazeFactor = max(dToFarAtmos - dToAtmos, 0.0) * hazeBrightness * 0.5 * nl;
|
||||
hazeFactor *= smoothstep(inFade,planetRim,ndv) * smoothstep(outerFade,inFade,ndv);
|
||||
rimHaze = hazeColor(ndl, gl_LightSource[0].diffuse.rgb * lightFactor) * hazeFactor;
|
||||
}
|
||||
float rimLum = max(rimHaze.r, max(rimHaze.g, rimHaze.b));
|
||||
|
||||
vec3 diffuse = gl_LightSource[0].diffuse.rgb * nl * lightFactor;
|
||||
diffuse += gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;
|
||||
|
||||
vec3 specular = vec3(0.0);
|
||||
{ //Light 0
|
||||
rDv = dot(normalize(-reflect(l, cloudNorm)),v);
|
||||
if(rDv > 0.0) {
|
||||
specular += gl_LightSource[0].specular.rgb * pow(rDv, gl_FrontMaterial.shininess) * lightFactor * pow(nl, 0.4);
|
||||
}
|
||||
}
|
||||
specular *= gl_FrontMaterial.specular.rgb;;
|
||||
|
||||
vec4 clouds = vec4(diffuse + specular, opacity);
|
||||
if(ndv < planetRim)
|
||||
clouds.a = 0.0;
|
||||
|
||||
//vec4 result = vec4(hazeFactor,0.0,0.0,1.0);
|
||||
vec4 result = vec4(0.0,0.0,0.0,shadow);
|
||||
if(hazeLum > 0.0)
|
||||
result = alphaBlend(result, vec4(haze / hazeLum, hazeLum * lowHaze));
|
||||
if(clouds.a > 0.0)
|
||||
result = alphaBlend(result, clouds);
|
||||
if(hazeLum > 0.0)
|
||||
result = alphaBlend(result, vec4(haze / hazeLum, hazeLum * hiHaze));
|
||||
if(rimLum > 0.0)
|
||||
result = alphaBlend(result, vec4(rimHaze / rimLum, rimLum));
|
||||
//result = vec4(shadow,0.0,0.0,1.0);
|
||||
|
||||
gl_FragColor = result;
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
#version 120
|
||||
|
||||
uniform sampler2D texture;
|
||||
|
||||
uniform float nodeScale;
|
||||
uniform vec3 colors[2];
|
||||
|
||||
varying vec2 uv;
|
||||
varying vec3 normal, binormal, tangent, light, light2;
|
||||
varying vec3 pos, center;
|
||||
varying float dist, dist2;
|
||||
|
||||
//planetRim is the (normal dot view) value corresponding to the edge of the planet on the atmosphere's model
|
||||
const float planetRim = 0.169;
|
||||
const float innerFadeStart = 0.169;
|
||||
const float innerFadeBase = planetRim + 0.021;
|
||||
const float innerFadeMax = 0.5;
|
||||
const float outerFade = 0.0;
|
||||
const float rimDecay = 2.0;
|
||||
|
||||
const float atmosExaggerateDist = 55.0;
|
||||
|
||||
//The thickness of atmosphere that must be present before any haze is visible
|
||||
const float hazeThreshold = 0.01;
|
||||
//The brightness multiplier of the haze
|
||||
const float hazeBrightnessBase = 15.0;
|
||||
const float scaledHazeBrightness = 60.0;
|
||||
const float rimHazeBrightness = 4.0;
|
||||
//Fraction of haze that occurs beneath the clouds
|
||||
const float lowHaze = 0.3;
|
||||
const float hiHaze = 1.0 - lowHaze;
|
||||
|
||||
//How much cover is 100% cloud cover
|
||||
const float shadowDarkness = 1.0;
|
||||
|
||||
const float shadowDist = 0.025;
|
||||
|
||||
vec3 hazeColor(float NdL, vec3 lightCol) {
|
||||
float depth = pow(max(0.8 - NdL, 0.0), 2.0);
|
||||
float lum = max(lightCol.r, max(lightCol.g, lightCol.b));
|
||||
vec3 col = mix(colors[0], colors[1], depth);
|
||||
col /= max(col.r, max(col.g, col.b));
|
||||
return col * lum * pow(max((0.8 + NdL) * 0.5, 0.0), 1.3);
|
||||
}
|
||||
|
||||
vec4 alphaBlend(vec4 dest, vec4 src) {
|
||||
float alpha = src.a + (dest.a * (1.0 - src.a));
|
||||
return vec4( ((src.rgb * src.a) + (dest.rgb * dest.a * (1.0 - src.a))) / alpha, alpha);
|
||||
}
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
void main() {
|
||||
vec3 normMap = texture2D(texture, uv).rgb - vec3(0.5);
|
||||
float opacity = max((length(normMap) - 0.25) / 0.25, 0.0);
|
||||
normMap.z = deriveZ(normMap.xy);
|
||||
|
||||
vec3 n = normalize(normal);
|
||||
|
||||
vec3 cloudNorm = n * normMap.z;
|
||||
cloudNorm += normalize(binormal) * normMap.x;
|
||||
cloudNorm += normalize(tangent) * normMap.y;
|
||||
cloudNorm = normalize(cloudNorm);
|
||||
|
||||
vec3 p = -pos;
|
||||
//Apply a correction to the position to correct for the vertex structure
|
||||
p += nodeScale * (length(normal) - 1.0) * n;
|
||||
|
||||
vec3 v = normalize(p);
|
||||
vec3 c = normalize(center);
|
||||
float cosTheta = -dot(c,v);
|
||||
float theta = acos(cosTheta);
|
||||
|
||||
float dToCenter = length(center);
|
||||
float dToAtmos = length(p);
|
||||
float dToFarAtmos;
|
||||
float dToSurface;
|
||||
{
|
||||
float k = sin(theta) * dToCenter;
|
||||
float e = sqrt(nodeScale*nodeScale - k*k);
|
||||
dToSurface = sqrt(dToCenter*dToCenter - k*k) - e;
|
||||
|
||||
e = sqrt(pow(nodeScale*1.015,2.0) - k*k);
|
||||
dToFarAtmos = dToAtmos + e*2.0;
|
||||
}
|
||||
|
||||
dToCenter /= nodeScale;
|
||||
dToAtmos /= nodeScale;
|
||||
dToFarAtmos /= nodeScale;
|
||||
dToSurface /= nodeScale;
|
||||
|
||||
float exag = clamp(pow(dToCenter / atmosExaggerateDist, 2.0), 0.0, 1.0);
|
||||
|
||||
float hazeBrightness = hazeBrightnessBase;
|
||||
hazeBrightness += scaledHazeBrightness * exag;
|
||||
float innerFade = planetRim + 0.021;
|
||||
|
||||
vec3 l = normalize(light);
|
||||
vec3 l2 = normalize(light2);
|
||||
float ndv = max(dot(n, v), 0.0);
|
||||
float ndl = dot(n,l);
|
||||
|
||||
//Calculate shadow cover
|
||||
float shadow = 0.0;
|
||||
if(ndl > 0.0 && ndv > planetRim) {
|
||||
vec2 off;
|
||||
off.x = asin(dot(binormal,l)) * shadowDist / 6.283;
|
||||
off.y = asin(dot(tangent,l)) * shadowDist / 6.283;
|
||||
|
||||
float thickness = max((length(texture2D(texture, uv + off.xy).rgb - vec3(0.5))-0.25)/0.25, 0.0) * sqrt(ndl);
|
||||
shadow = thickness * shadowDarkness;
|
||||
}
|
||||
|
||||
float lightFactor = 1.0 / (1.0 + (gl_LightSource[0].quadraticAttenuation * dist * dist));
|
||||
vec3 r = normalize(-reflect(l, n));
|
||||
float rDv = dot(r,v);
|
||||
|
||||
//Calculate atmospheric haze
|
||||
//Haze increases in brightness with depth through the atmosphere
|
||||
//The color changes as light is preferentially scattered away by frequency
|
||||
|
||||
vec3 haze = vec3(0.0);
|
||||
if(ndv >= planetRim + 0.00001) {
|
||||
float rimFactor = max(dToFarAtmos - dToAtmos - hazeThreshold, 0.0) * 0.15 * rimHazeBrightness;
|
||||
float hazeFactor = max(dToSurface - dToAtmos - hazeThreshold, 0.0) * hazeBrightness;
|
||||
float factor = mix(rimFactor,hazeFactor,smoothstep(planetRim,innerFade, ndv));
|
||||
haze = hazeColor(ndl, gl_LightSource[0].diffuse.rgb * lightFactor) * factor;
|
||||
}
|
||||
else if(ndv >= planetRim) {
|
||||
//At the very edge of the planet, the surface distance can be invalid
|
||||
float hazeFactor = max(dToFarAtmos - dToAtmos - hazeThreshold, 0.0) * 0.15 * rimHazeBrightness ;
|
||||
haze = hazeColor(ndl, gl_LightSource[0].diffuse.rgb * lightFactor) * hazeFactor;
|
||||
}
|
||||
float hazeLum = min(max(haze.r, max(haze.g, haze.b)), 1.0);
|
||||
|
||||
vec3 rimHaze = vec3(0.0);
|
||||
if(ndv <= innerFade) {
|
||||
float hazeFactor = max(dToFarAtmos - dToAtmos - hazeThreshold, 0.0) * rimHazeBrightness * 0.5;
|
||||
float surfFactor = max((dToFarAtmos - dToAtmos)*0.5 - hazeThreshold, 0.0) * hazeBrightness;
|
||||
hazeFactor = mix(hazeFactor,surfFactor,ndv/planetRim);
|
||||
|
||||
hazeFactor *= smoothstep(innerFade,innerFadeStart,ndv) * pow(min(1.0, ndv/planetRim), rimDecay);
|
||||
rimHaze = hazeColor(ndl, gl_LightSource[0].diffuse.rgb * lightFactor) * hazeFactor;
|
||||
}
|
||||
rimHaze += haze * hiHaze;
|
||||
float rimLum = min(max(rimHaze.r, max(rimHaze.g, rimHaze.b)), 1.0);
|
||||
|
||||
float nDl = dot(cloudNorm,light);
|
||||
float nl = max(0.0, nDl);
|
||||
|
||||
vec3 diffuse = gl_LightSource[0].diffuse.rgb * nl * lightFactor * gl_FrontMaterial.diffuse.rgb;
|
||||
diffuse += gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;
|
||||
|
||||
vec3 specular = vec3(0.0);
|
||||
{ //Light 0
|
||||
rDv = dot(normalize(-reflect(l, cloudNorm)),v);
|
||||
if(rDv > 0.0) {
|
||||
specular += gl_LightSource[0].specular.rgb * pow(rDv, gl_FrontMaterial.shininess) * lightFactor * pow(nl, 0.4);
|
||||
}
|
||||
}
|
||||
{ //Light 1
|
||||
rDv = dot(normalize(-reflect(l2, cloudNorm)),v);
|
||||
float l2Factor = 1.0 / (1.0 + (gl_LightSource[1].quadraticAttenuation * dist2 * dist2));
|
||||
diffuse += gl_LightSource[1].diffuse.rgb * max(0.0, dot(cloudNorm,l2)) * l2Factor;
|
||||
if(rDv > 0.0) {
|
||||
specular += gl_LightSource[1].specular.rgb * pow(rDv, gl_FrontMaterial.shininess) * l2Factor * pow(nl, 0.4);
|
||||
}
|
||||
}
|
||||
specular *= gl_FrontMaterial.specular.rgb;;
|
||||
|
||||
vec4 clouds = vec4(diffuse + specular, opacity);
|
||||
if(ndv < planetRim)
|
||||
clouds.a = 0.0;
|
||||
|
||||
//vec4 result = vec4(hazeFactor,0.0,0.0,1.0);
|
||||
vec4 result = vec4(0.0,0.0,0.0,shadow);
|
||||
if(hazeLum > 0.0)
|
||||
result = alphaBlend(result, vec4(haze / hazeLum, hazeLum * lowHaze));
|
||||
if(clouds.a > 0.0)
|
||||
result = alphaBlend(result, clouds);
|
||||
//if(hazeLum > 0.0)
|
||||
// result = alphaBlend(result, vec4(haze / hazeLum, hazeLum * hiHaze);
|
||||
if(rimLum > 0.0)
|
||||
result = alphaBlend(result, vec4(rimHaze / rimLum, rimLum));
|
||||
//result = vec4(shadow,0.0,0.0,1.0);
|
||||
|
||||
result.a *= clamp(ndv / clamp(exag * 2.0, 0.00001, 0.3), 0.0, 1.0);
|
||||
|
||||
gl_FragColor = result;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
attribute vec4 in_position;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
|
||||
varying vec2 uv;
|
||||
varying vec3 pos, center;
|
||||
varying vec3 normal, binormal, tangent, light, light2;
|
||||
varying float dist, dist2;
|
||||
|
||||
uniform float nodeScale;
|
||||
|
||||
void main()
|
||||
{
|
||||
normal = normalize(gl_NormalMatrix * in_normal);
|
||||
binormal = normalize(cross(normal, gl_NormalMatrix * vec3(0.0,0.999,0.04471017781)));
|
||||
tangent = normalize(cross(normal, binormal));
|
||||
vec4 vertPos = gl_ModelViewMatrix * in_position;
|
||||
pos = vertPos.xyz;
|
||||
|
||||
center = (gl_ModelViewMatrix * vec4(0.0,0.0,0.0,1.0)).xyz;
|
||||
|
||||
vec4 lpos = gl_LightSource[0].position;
|
||||
vec4 lpos2 = gl_LightSource[1].position;
|
||||
vec4 s = normalize(lpos-vertPos);
|
||||
vec4 s2 = normalize(lpos2-vertPos);
|
||||
|
||||
light = s.xyz;
|
||||
light2 = s2.xyz;
|
||||
|
||||
dist = distance(vertPos, lpos);
|
||||
dist2 = distance(vertPos, lpos2);
|
||||
|
||||
uv = in_uv;
|
||||
gl_Position = gl_ProjectionMatrix * vertPos;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
varying vec2 uv;
|
||||
uniform vec4 color;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(texture,uv) * color;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(texture,uv);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
attribute vec4 in_position;
|
||||
attribute vec2 in_uv;
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = in_uv;
|
||||
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * in_position;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(texture,uv) * gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
gl_Position = ftransform();
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
varying vec2 uv;
|
||||
uniform float cycle;
|
||||
uniform float offset;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main() {
|
||||
vec3 sample = texture2D(texture,uv - vec2(cycle, 0.0)).rgb * (1.0 - uv.x);
|
||||
|
||||
if(abs((uv.x + 1.0) - (mod(cycle + offset, 1.0) * 2.0)) < 0.3)
|
||||
sample *= 1.3;
|
||||
|
||||
gl_FragColor.rgb = ((sample - vec3(0.5)) * 2.0 + gl_Color.rgb) * gl_Color.a;
|
||||
gl_FragColor.a = 0.0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
|
||||
//Masks: R = Glow, G = Owner Color, B = Engine Glow
|
||||
uniform sampler2D diffuse;
|
||||
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
//vec3 color = gl_FrontMaterial.diffuse.rgb;
|
||||
vec3 texSamp = texture2D(diffuse, uv.xy).rgb;
|
||||
|
||||
float value = max(texSamp.r, max(texSamp.g, texSamp.b));
|
||||
|
||||
//Shrink value range
|
||||
value = mix(0.5, 1.0, value);
|
||||
|
||||
gl_FragColor.rgb = value * vec3(0.1,0.1,1.0);
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec2 in_uv;
|
||||
|
||||
varying vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
uv = in_uv;
|
||||
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * in_vertex;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
varying vec2 rcoords;
|
||||
|
||||
void main() {
|
||||
float r = length(rcoords);
|
||||
if(r > 1.0)
|
||||
discard;
|
||||
|
||||
vec4 col = gl_Color;
|
||||
col.a *= 1.0 - smoothstep(0.95, 1.0, r);
|
||||
gl_FragColor = col;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
gl_FragColor.rgb = gl_Color.rgb * gl_Color.a;
|
||||
gl_FragColor.a = 0.0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main() {
|
||||
gl_FragColor.rgb = (texture2D(texture,uv).rgb * gl_Color.rgb) * gl_Color.a;
|
||||
gl_FragColor.a = 0.0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
uniform vec4 color;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(texture,uv) * color;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
uniform float selected;
|
||||
|
||||
void main() {
|
||||
vec4 col = texture2D(texture, uv) * gl_Color;
|
||||
col.rgb = mix(col.rgb, vec3(1.0, 1.0, 1.0), selected * 0.5);
|
||||
gl_FragColor = col;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
uniform sampler2D texture;
|
||||
uniform float time, temperature, scale;
|
||||
uniform vec4 color;
|
||||
varying vec2 uv;
|
||||
varying vec2 rcoords;
|
||||
varying vec3 viewDir, pos;
|
||||
const float innerRadius = 1.95/3.0;
|
||||
|
||||
//Severity of changes in the corona
|
||||
const float turbulence = 9.0;
|
||||
//Brightness of the corona flames
|
||||
const float baseBrightness = 0.3, flameMinBrightness = 1.0, flameMaxBrightness = 1.3;
|
||||
//Brightness of the haze
|
||||
const float auraBrightness = 0.3;
|
||||
|
||||
void main() {
|
||||
float r = (length(rcoords) - innerRadius) / (1.0 - innerRadius);
|
||||
if(r > 1.0)
|
||||
discard;
|
||||
else if(r < -0.05)
|
||||
discard;
|
||||
|
||||
float d = length(pos);
|
||||
float baseDist = 1.0 * scale;
|
||||
vec3 col = color.rgb * smoothstep(1.0,0.0,pow(r,0.5)) * auraBrightness * pow(2.0 - r, (8.0*baseDist + d)/(baseDist + d) + 0.2);
|
||||
|
||||
vec3 p = normalize(viewDir);
|
||||
|
||||
float temp = clamp((temperature - 4000.0) / 25800.0, 0.0, 1.0);
|
||||
float t = time * (1.0 + floor(temp * 2.5 + 0.5));
|
||||
|
||||
float u = t * 24.0/6.0 + atan(rcoords.y, rcoords.x) / 6.2828;
|
||||
float v = u * 6.0 - t * 25.0;
|
||||
|
||||
vec3 noise = texture2D(texture,vec2(u,v), -10.0).rgb;
|
||||
|
||||
float n = mix(noise.r, noise.g, abs(atan(p.z,p.x)/6.2828) * 2.0);
|
||||
n = (mix(n, noise.b, abs(p.z)) + baseBrightness) * mix(flameMinBrightness, flameMaxBrightness, temp * (1.0 - r));
|
||||
|
||||
float corona = pow(n,turbulence);
|
||||
corona = pow(corona,(0.15+(r*1.85))) * (1.0 + r) * 0.5;
|
||||
corona *= pow(1.0 - smoothstep(0.0,1.0,r), 4.0) * 0.8;
|
||||
|
||||
col += color.rgb * max(corona, 0.0);
|
||||
|
||||
gl_FragColor.rgb = col;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
varying vec2 uv;
|
||||
varying vec2 rcoords;
|
||||
varying vec3 viewDir, pos;
|
||||
uniform vec4 sprite_pos;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
rcoords = ((uv - sprite_pos.xy) / (sprite_pos.zw - sprite_pos.xy) - vec2(0.5, 0.5)) * 2.0;
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
|
||||
pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
|
||||
viewDir = (gl_NormalMatrix * vec3(1.0,0.0,0.0)).xyz;
|
||||
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
varying vec2 uv;
|
||||
varying float renderpos;
|
||||
|
||||
uniform sampler2D texture;
|
||||
uniform float cutoff;
|
||||
|
||||
void main() {
|
||||
if(renderpos > (cutoff * 2.0) - 1.0)
|
||||
discard;
|
||||
gl_FragColor.rgb = texture2D(texture, uv).rgb;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
varying vec2 uv;
|
||||
attribute vec2 in_uv;
|
||||
varying float renderpos;
|
||||
|
||||
void main() {
|
||||
uv = in_uv;
|
||||
gl_Position = ftransform();
|
||||
|
||||
renderpos = gl_Vertex.x;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
uniform float level;
|
||||
const vec3 gray = vec3(0.30, 0.59, 0.11);
|
||||
|
||||
void main() {
|
||||
vec4 color = texture2D(texture, uv);
|
||||
gl_FragColor.rgb = mix(vec3(dot(gray, color.rgb * gl_Color.rgb)), color.rgb * gl_Color.rgb, level);
|
||||
gl_FragColor.a = color.a * gl_Color.a;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
float radius = length(uv);
|
||||
if(radius > 1.0)
|
||||
discard;
|
||||
gl_FragColor = gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
float rad = length(uv - vec2(0.5));
|
||||
if(rad > 0.33)
|
||||
discard;
|
||||
gl_FragColor = vec4(gl_Color.rgb, (1.0 - max((rad - 0.28) / 0.05, 0)) * gl_Color.a);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
|
||||
vec4 cpos = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
cpos.z -= 0.002;
|
||||
gl_Position = cpos;
|
||||
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
varying vec2 uv, uv1, uv2;
|
||||
uniform sampler2D textures[3];
|
||||
uniform vec4 sprite1;
|
||||
uniform vec4 sprite2;
|
||||
uniform float fade;
|
||||
|
||||
vec4 alphaBlend(vec4 dest, vec4 src) {
|
||||
float alpha = src.a + (dest.a * (1.0 - src.a));
|
||||
return vec4( ((src.rgb * src.a) + (dest.rgb * dest.a * (1.0 - src.a))) / alpha, alpha);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 result = texture2D(textures[0],uv);
|
||||
result.rgb *= gl_Color.rgb;
|
||||
|
||||
vec4 icon1 = texture2D(textures[1], mix(sprite1.xy, sprite1.zw, uv1));
|
||||
icon1.rgb = (fade * icon1.rgb) + ((1.0 - fade) * gl_Color.rgb);
|
||||
if(all(bvec4( greaterThanEqual(uv1, vec2(0.0)), lessThanEqual(uv1, vec2(1.0)))))
|
||||
result = alphaBlend( result, icon1 );
|
||||
|
||||
vec4 icon2 = texture2D(textures[2], mix(sprite2.xy, sprite2.zw, uv2));
|
||||
icon2.a *= fade;
|
||||
if(all(bvec4( greaterThanEqual(uv2, vec2(0.0)), lessThanEqual(uv2, vec2(1.0)))))
|
||||
result = alphaBlend( result, icon2 );
|
||||
|
||||
result.a *= gl_Color.a;
|
||||
gl_FragColor = result;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
varying vec2 uv, uv1, uv2;
|
||||
uniform float approach;
|
||||
uniform float innerScale1;
|
||||
uniform float innerScale2;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
uv1 = ((uv - vec2(0.5)) / innerScale1) + vec2(0.5);
|
||||
uv2 = ((uv - vec2(0.5)) / innerScale2) + vec2(0.5);
|
||||
|
||||
vec4 cpos = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
cpos.z -= approach;
|
||||
gl_Position = cpos;
|
||||
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#version 120
|
||||
#define pi 3.141592653589793238462643383279
|
||||
#define twopi (pi * 2.0)
|
||||
varying vec2 uv, uv1, uv2;
|
||||
uniform sampler2D textures[4];
|
||||
uniform vec4 sprite1;
|
||||
uniform vec4 sprite2;
|
||||
uniform float fade;
|
||||
uniform float isDisabled;
|
||||
uniform float isUsed;
|
||||
uniform float isExported;
|
||||
uniform float isColonizing;
|
||||
uniform float isOwned;
|
||||
uniform float isDecaying;
|
||||
uniform float classIcon;
|
||||
uniform vec4 captureColor;
|
||||
uniform float capturePct;
|
||||
uniform vec4 disableColor;
|
||||
|
||||
const float w = 128.0;
|
||||
const float h = 256.0;
|
||||
const float modRatio = 26.0/21.0;
|
||||
const float modShift = 0.09;
|
||||
const float row = 4.0;
|
||||
const vec4 disabled_icon = vec4(28.0/w, 81.0/h, 55.0/w, 110.0/h);
|
||||
const vec4 unused_icon = vec4(83.0/w, 2.0/h, 108.0/w, 27.0/h);
|
||||
const vec4 decay_icon = vec4(29.0/w, 56.0/h, 54.0/w, 81.0/h);
|
||||
const vec4 exported_icon = vec4(29.0/w, 2.0/h, 54.0/w, 27.0/h);
|
||||
const vec4 colonizing_icon = vec4(2.0/w, 56.0/h, 29.0/w, 81.0/h);
|
||||
const vec4 class_icon = vec4(2.0/w, 29.0/h, 27.0/w, 54.0/h);
|
||||
const vec2 class_offset = vec2(27.0/w, 27.0/h);
|
||||
|
||||
void main() {
|
||||
vec4 result = texture2D(textures[0],uv);
|
||||
|
||||
vec2 uv3 = (uv2 / modRatio) + vec2(modShift);
|
||||
vec4 disIcon = texture2D(textures[3], mix(disabled_icon.xy, disabled_icon.zw, uv1)) * disableColor;
|
||||
vec4 uIcon = texture2D(textures[3], mix(unused_icon.xy, unused_icon.zw, uv3));
|
||||
vec4 dIcon = texture2D(textures[3], mix(decay_icon.xy, decay_icon.zw, uv3));
|
||||
vec4 expIcon = texture2D(textures[3], mix(exported_icon.xy, exported_icon.zw, uv3));
|
||||
vec2 classOff = vec2(class_offset.x * mod(classIcon - 4.0, row), class_offset.y * floor((classIcon - 4.0)/row));
|
||||
vec4 clsIcon = texture2D(textures[3], mix(class_icon.xy+classOff, class_icon.zw+classOff, uv3));
|
||||
vec4 colIcon = texture2D(textures[3], mix(colonizing_icon.xy, colonizing_icon.zw, uv3-vec2(-0.4, -0.4)));
|
||||
|
||||
result.rgb *= gl_Color.rgb;
|
||||
|
||||
//Base of black transparent if unowned
|
||||
result *= isOwned;
|
||||
|
||||
vec4 icon1 = texture2D(textures[1], mix(sprite1.xy, sprite1.zw, uv1));
|
||||
|
||||
icon1.rgb = (fade * icon1.rgb) + ((1.0 - fade) * gl_Color.rgb);
|
||||
if(all(bvec4( greaterThanEqual(uv1, vec2(0.0)), lessThanEqual(uv1, vec2(1.0))))) {
|
||||
result = mix( result, vec4(icon1.rgb, 1.0), icon1.a );
|
||||
result = mix( result, vec4(disIcon.rgb, 1.0), disIcon.a * fade * isDisabled );
|
||||
}
|
||||
|
||||
vec4 icon2 = texture2D(textures[2], mix(sprite2.xy, sprite2.zw, uv2));
|
||||
if(all(bvec4( greaterThanEqual(uv2, vec2(0.0)), lessThanEqual(uv2, vec2(1.0))))) {
|
||||
result = mix( result, vec4(icon2.rgb, 1.0), icon2.a * fade );
|
||||
}
|
||||
|
||||
if(all(bvec4( greaterThanEqual(uv3, vec2(0.0)), lessThanEqual(uv3, vec2(1.0))))) {
|
||||
vec2 showCls = step(vec2(3.0, classIcon), vec2(classIcon, 20.0));
|
||||
result = mix( result, vec4(clsIcon.rgb, 1.0), clsIcon.a * fade * showCls.x * showCls.y );
|
||||
result = mix( result, vec4(dIcon.rgb, 1.0), dIcon.a * fade * isDecaying );
|
||||
result = mix( result, vec4(uIcon.rgb, 1.0), uIcon.a * fade * (1.0 - isUsed) );
|
||||
result = mix( result, vec4(expIcon.rgb, 1.0), expIcon.a * fade * isExported );
|
||||
}
|
||||
|
||||
if(all(bvec4( greaterThanEqual(uv3, vec2(-0.4, -0.4)), lessThanEqual(uv3, vec2(0.6, 0.4))))) {
|
||||
result = mix( result, vec4(colIcon.rgb, 1.0), colIcon.a * fade * isColonizing );
|
||||
}
|
||||
|
||||
if(captureColor.a > 0.0) {
|
||||
vec2 rcoords = (uv - vec2(0.5, 0.5)) * 2.0;
|
||||
float radius = length(rcoords);
|
||||
if(radius > 0.8 && radius < 1.0) {
|
||||
float ang = (atan(rcoords.x, rcoords.y) + pi) / twopi;
|
||||
if(ang < capturePct) {
|
||||
/*float alpha = sqrt((radius - 0.8) * 5.0);*/
|
||||
float alpha = 1.0 - (abs(radius - 0.9) * 11.0);
|
||||
result = mix( result, vec4(captureColor.rgb, 1.0), alpha * captureColor.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.a *= gl_Color.a;
|
||||
gl_FragColor = result;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#define pi 3.141592653589793238462643383279
|
||||
#define twopi (pi * 2.0)
|
||||
uniform float minRad;
|
||||
uniform float maxRad;
|
||||
varying vec2 rcoords;
|
||||
|
||||
void main() {
|
||||
float r = length(rcoords);
|
||||
float width = 0.7;
|
||||
if(r >= 1.0 || r < 1.0 - width)
|
||||
discard;
|
||||
|
||||
float ang = atan(rcoords.y, rcoords.x);
|
||||
if(ang < minRad && ang > maxRad - twopi)
|
||||
discard;
|
||||
if(ang > maxRad && ang < minRad + twopi)
|
||||
discard;
|
||||
|
||||
float bdist = r - (1.0 - width);
|
||||
|
||||
vec4 col = gl_Color;
|
||||
if(r < 0.95)
|
||||
col.a *= 0.5;
|
||||
else if(r < 0.96)
|
||||
col.a *= 0.5 + ((r - 0.95) / 0.01) * 0.5;
|
||||
else if(r > 0.99)
|
||||
col.a *= (1.0 - r) / 0.01;
|
||||
|
||||
col.a *= bdist / width;
|
||||
|
||||
float adist = min(abs(ang - minRad), abs(ang - maxRad));
|
||||
if(adist < 0.02)
|
||||
col.a *= adist / 0.02;
|
||||
|
||||
gl_FragColor = col;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
uniform sampler2D texture;
|
||||
varying vec2 rcoords;
|
||||
const float twopi = 6.28318530718;
|
||||
|
||||
void main() {
|
||||
float r = length(rcoords);
|
||||
if(r >= 1.0 || r < 0.0)
|
||||
discard;
|
||||
|
||||
float a = atan(rcoords.y, rcoords.x) / twopi;
|
||||
vec4 col = gl_Color;
|
||||
col.a *= texture2D(texture, vec2(a,r)).r;
|
||||
if(col.a < 1.0 / 255.0)
|
||||
discard;
|
||||
|
||||
gl_FragColor = col;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
uniform float tOffset;
|
||||
uniform float time[2];
|
||||
uniform vec4 color;
|
||||
const float twopi = 6.2828;
|
||||
const float range = 0.25;
|
||||
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
gl_Position = ftransform();
|
||||
|
||||
vec4 col = color * ((1.0 - range) + range * sin((time[0] + tOffset) * twopi) * cos((time[1] + tOffset) * twopi));
|
||||
|
||||
gl_FrontColor = col;
|
||||
gl_BackColor = col;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
uniform float tOffset;
|
||||
uniform float time[2];
|
||||
const float twopi = 6.2828;
|
||||
const float range = 0.25;
|
||||
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
gl_Position = ftransform();
|
||||
|
||||
vec4 col = gl_Color * ((1.0 - range) + range * sin((time[0] + tOffset) * twopi) * cos((time[1] + tOffset) * twopi));
|
||||
|
||||
gl_FrontColor = col;
|
||||
gl_BackColor = col;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#version 120
|
||||
uniform sampler2D screen;
|
||||
varying vec2 uv;
|
||||
|
||||
const float blurStep = 0.001;
|
||||
const vec3 gray = vec3(0.50, 0.50, 0.50);
|
||||
const float desaturateLevel = 0.6;
|
||||
|
||||
void main()
|
||||
{
|
||||
//Blur the pixel
|
||||
vec4 color = texture2D(screen, uv);
|
||||
color += texture2D(screen, uv + vec2(-blurStep, -blurStep));
|
||||
color += texture2D(screen, uv + vec2(0, -blurStep));
|
||||
color += texture2D(screen, uv + vec2(+blurStep, -blurStep));
|
||||
|
||||
color += texture2D(screen, uv + vec2(-blurStep, 0));
|
||||
color += texture2D(screen, uv + vec2(+blurStep, 0));
|
||||
|
||||
color += texture2D(screen, uv + vec2(-blurStep, +blurStep));
|
||||
color += texture2D(screen, uv + vec2(0, +blurStep));
|
||||
color += texture2D(screen, uv + vec2(+blurStep, +blurStep));
|
||||
color /= 9.0;
|
||||
|
||||
//Desaturate the pixel
|
||||
color.rgb = mix(vec3(dot(gray, color.rgb)), color.rgb, desaturateLevel);
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#version 120
|
||||
uniform sampler2D screen;
|
||||
varying vec2 uv;
|
||||
|
||||
const float blurStep = 0.001;
|
||||
const vec3 gray = vec3(0.50, 0.50, 0.50);
|
||||
const float desaturateLevel = 0.6;
|
||||
|
||||
void main()
|
||||
{
|
||||
//Blur the pixel
|
||||
vec4 color = texture2D(screen, uv);
|
||||
color += texture2D(screen, uv + vec2(-blurStep, -blurStep));
|
||||
color += texture2D(screen, uv + vec2(0, -blurStep));
|
||||
color += texture2D(screen, uv + vec2(+blurStep, -blurStep));
|
||||
|
||||
color += texture2D(screen, uv + vec2(-blurStep, 0));
|
||||
color += texture2D(screen, uv + vec2(+blurStep, 0));
|
||||
|
||||
color += texture2D(screen, uv + vec2(-blurStep, +blurStep));
|
||||
color += texture2D(screen, uv + vec2(0, +blurStep));
|
||||
color += texture2D(screen, uv + vec2(+blurStep, +blurStep));
|
||||
color /= 9.0;
|
||||
|
||||
//Desaturate the pixel
|
||||
color.rgb = mix(vec3(dot(gray, color.rgb)), color.rgb, desaturateLevel);
|
||||
|
||||
//Darkened edge
|
||||
float dist = length(uv - vec2(0.5, 0.5));
|
||||
if(dist > 0.4)
|
||||
color.rgb = mix(color.rgb, vec3(0.0, 0.0, 0.0), (dist - 0.4)/0.3);
|
||||
|
||||
gl_FragColor = color * gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
uniform sampler2D screen;
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(screen, uv);
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
#version 120
|
||||
|
||||
// expose these as tick boxes, suggested defaults are set to true
|
||||
// godrays from stars, scales with shader level setting
|
||||
bool lightShaftsOn = #{{bGodRays}};
|
||||
// bloom, scales with shader level setting
|
||||
bool bloomOn = #{{bBloom}};
|
||||
// darkend edges, brighten center on all zoom levels
|
||||
bool vignetteOn = #{{bVignette}};
|
||||
// sharpen function, on zoom out, to increase icon readability without and especially with bloom
|
||||
bool sharpen = #{{bBloom}};
|
||||
// "movie feel" radial color fringe effect, only up close in systems
|
||||
bool chromaticAberrationOn = #{{bChromaticAberration}};
|
||||
// "movie feel" grain everywhere
|
||||
bool filmGrain = #{{bFilmGrain}};
|
||||
|
||||
//must be odds for bloom!
|
||||
const int bloomPasses = #{{level:extreme}} ? 9 : (#{{level:high}} ? 7 : 5);
|
||||
const int rayPasses = #{{level:extreme}} ? 24 : (#{{level:high}} ? 20 : 16);
|
||||
|
||||
const int nLightCount = 2;
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
uniform sampler2D screen, depthTex;
|
||||
uniform vec2 texSize;
|
||||
uniform vec2 lightPos[nLightCount];
|
||||
uniform float lightActive[nLightCount]; // might need to implement a kill switch
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform float cycle;
|
||||
|
||||
varying vec2 aspectRatio;
|
||||
varying vec2 uv;
|
||||
varying vec4 lightVec[nLightCount];
|
||||
varying vec2 radialAnimation[nLightCount];
|
||||
varying vec4 camVec, camDirection[nLightCount];
|
||||
varying vec3 lightColor[nLightCount];
|
||||
varying vec4 pos;
|
||||
|
||||
varying float lightDepth[nLightCount], fallOffDepth[nLightCount];
|
||||
|
||||
|
||||
// none of these needs to be exposed
|
||||
const float sharpness = 0.25; // sharpen intensity
|
||||
const float chromaticPower = 1.0; // chromatic fringe intensity
|
||||
const float vignetteFactor = 0.5; // power of vignette on un-bloomed screen.
|
||||
const float bloomTreshold = 0.25; // how low a value does blooming start
|
||||
const float bloomIntensity = 1.5; // intensity of the blooming.
|
||||
const float bloomScale = 4.0; // scale of bloom!
|
||||
const float rayScale = 0.006;
|
||||
const float rayIntensity = 0.75;
|
||||
const float frameSize = 32.0; // fade power that reduces bloom near the edges to clean bad bloom from wrapping - less is more!
|
||||
const float filmGrainIntensity = 2.0;
|
||||
// random noise functions ahead
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
// sine stabilized rand for film grain
|
||||
float rand( vec2 n ){
|
||||
return fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453);
|
||||
}
|
||||
|
||||
float hash12(vec2 p){
|
||||
vec3 p3 = fract(vec3(p.xyx) * hashSeed.xyz);
|
||||
p3 += dot(p3, p3.yzx + 19.19);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
float noise(vec2 n){
|
||||
const vec2 d = vec2(0.0, 1.0);
|
||||
vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n));
|
||||
return mix(mix(hash12(b), hash12(b + d.yx), f.x), mix(hash12(b + d.xy), hash12(b + d.yy), f.x), f.y);
|
||||
}
|
||||
|
||||
vec3 texture(vec2 uv){
|
||||
return texture2D(screen,uv).rgb;
|
||||
}
|
||||
|
||||
vec3 square(vec3 x){
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x){
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow4(float x){
|
||||
x *= x;
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float linearFalloff(vec2 x){
|
||||
x *= x;
|
||||
return pow(x.x + x.y, 0.5);
|
||||
}
|
||||
|
||||
float pow32(float x){
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
vec3 toLinear(vec3 color){
|
||||
return pow(color, vec3(2.2));
|
||||
}
|
||||
|
||||
vec4 toGamma(vec4 color){
|
||||
return pow(color, vec4(0.45));
|
||||
}
|
||||
|
||||
vec4 toLinear(vec4 color){
|
||||
return pow(color, vec4(2.2));
|
||||
}
|
||||
|
||||
float toLinear(float color){
|
||||
return pow(color, 2.2);
|
||||
}
|
||||
|
||||
float toGamma(float color){
|
||||
return pow(color, 0.45);
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 color){
|
||||
return pow(color, vec3(0.45));
|
||||
}
|
||||
|
||||
float dotter(vec3 x){
|
||||
return dot(x,x);
|
||||
}
|
||||
|
||||
float dotter(vec2 x){
|
||||
return dot(x,x);
|
||||
}
|
||||
|
||||
float dotter(vec4 x){
|
||||
return dot(x,x);
|
||||
}
|
||||
|
||||
float normpdf(float x, float sigma){
|
||||
return 0.39894*exp(-0.5 * x * x / (sigma * sigma)) / sigma;
|
||||
}
|
||||
|
||||
vec3 vignette(vec3 color, float radialFade){
|
||||
return color * ((1.0 - (1.0 - radialFade) * vignetteFactor) + vignetteFactor * 0.5 * vec3(0.9));
|
||||
}
|
||||
|
||||
vec3 redShift(float t){
|
||||
return 0.5 + 0.5 * cos( tau *(t+ vec3(0.0, 0.1, 0.2)) );
|
||||
}
|
||||
|
||||
void main(){
|
||||
vec2 centeredUV = uv - 0.5;
|
||||
float frame = pow(abs(centeredUV.x)* 2.0, frameSize);
|
||||
frame = clamp(mix(pow(abs(centeredUV.y)* 2.0, frameSize), frame, frame) * 2.0 - 0.5, 0.0,1.0);
|
||||
float radialFade = 1.0 - min(1.0, dot(centeredUV, centeredUV) * 2.0);
|
||||
|
||||
vec4 color = texture2D(screen, uv);
|
||||
vec3 sum = vec3(0.0);
|
||||
vec3 rawColor = color.rgb;
|
||||
vec3 linearColor = toLinear(rawColor);
|
||||
float depth = texture2D(depthTex, uv).r;
|
||||
|
||||
// calculate falloff as it's needed in multiple places
|
||||
float falloff[2] = float[2](float(0.0), float(0.0));
|
||||
for (int i = 0; i < nLightCount; i++){
|
||||
falloff[i] = clamp((1.0 / fallOffDepth[i]) * (lightRadius[i] * 2.0), 0.0, 1.0);
|
||||
}
|
||||
|
||||
// kill mask for effects either in system only or galactic zoom only
|
||||
float inSystemKill = square(1.0 - min(1.0, (falloff[0] + falloff[1]) * 10.0));
|
||||
|
||||
if (sharpen == true){
|
||||
|
||||
vec2 step = 1.0 / texSize.xy;
|
||||
|
||||
vec3 sampA = toLinear(texture(uv + vec2(-step.x, -step.y) * 1.5 ));
|
||||
vec3 sampB = toLinear(texture(uv + vec2( step.x, -step.y) * 1.5 ));
|
||||
vec3 sampC = toLinear(texture(uv + vec2(-step.x, step.y) * 1.5 ));
|
||||
vec3 sampD = toLinear(texture(uv + vec2( step.x, step.y) * 1.5 ));
|
||||
|
||||
vec3 around = 0.25 * (sampA + sampB + sampC + sampD);
|
||||
|
||||
color.rgb = toGamma(linearColor + (linearColor - around) * mix(0.0, sharpness, inSystemKill));
|
||||
}
|
||||
|
||||
if (chromaticAberrationOn == true)
|
||||
{
|
||||
vec3 refractiveIndex = 1.0 + vec3(0.002, 0.004, 0.006) * mix(chromaticPower, 0.0, inSystemKill) * min(1.0, square((1.0 - depth) * 1024));
|
||||
vec3 texVec = vec3(uv * 2.0 - 1.0, 1.0);
|
||||
vec3 normalVec = vec3(0.0, 0.0, -1.0);
|
||||
vec3 redRefractionVec = refract(texVec, normalVec, refractiveIndex.r);
|
||||
vec3 greenRefractionVec = refract(texVec, normalVec, refractiveIndex.g);
|
||||
vec3 blueRefractionVec = refract(texVec, normalVec, refractiveIndex.b);
|
||||
vec2 redTexCoord = clamp(((redRefractionVec / redRefractionVec.z).xy + vec2(1.0)) / vec2(2.0), vec2(0.0), vec2(1.0));
|
||||
vec2 greenTexCoord = clamp(((greenRefractionVec / greenRefractionVec.z).xy + vec2(1.0)) / vec2(2.0), vec2(0.0), vec2(1.0));
|
||||
vec2 blueTexCoord = clamp(((blueRefractionVec / blueRefractionVec.z).xy + vec2(1.0)) / vec2(2.0), vec2(0.0), vec2(1.0));
|
||||
|
||||
color.rgb =
|
||||
mix(mix(vec3(
|
||||
texture2D(screen, redTexCoord).r,
|
||||
texture2D(screen, greenTexCoord).g,
|
||||
texture2D(screen, blueTexCoord).b)
|
||||
, color.rgb, radialFade), color.rgb, frame);
|
||||
}
|
||||
|
||||
if (bloomOn == true){
|
||||
|
||||
const int mSize = bloomPasses;
|
||||
const int kSize = (mSize-1)/2;
|
||||
|
||||
float kernel[mSize];
|
||||
float sigma = float(mSize);
|
||||
|
||||
float divider = 0.0;
|
||||
for (int j = 0; j <= kSize; ++j)
|
||||
{
|
||||
kernel[kSize+j] = kernel[kSize-j] = normpdf(float(j), sigma);
|
||||
}
|
||||
|
||||
for (int j = 0; j < mSize; ++j)
|
||||
{
|
||||
divider += kernel[j];
|
||||
}
|
||||
|
||||
for (int i=-kSize; i <= kSize; ++i)
|
||||
{
|
||||
for (int j=-kSize; j <= kSize; ++j)
|
||||
{
|
||||
sum += kernel[kSize+j] * kernel[kSize+i] * texture(uv +vec2(float(i),float(j)) / (texSize / (bloomScale)));
|
||||
}
|
||||
}
|
||||
|
||||
sum /= square(divider);
|
||||
|
||||
// kill bloom on the edges, where it will sample the other side from texture wrapping, and smoothstep
|
||||
sum = mix(sum * sum * (3.0 - 2.0 * sum), color.rgb, frame);
|
||||
// color correct and blend
|
||||
sum = toGamma((toLinear(color.rgb) + toLinear(max(vec3(0.0), sum * bloomIntensity - bloomTreshold) * 0.5)));
|
||||
color.rgb = sum;
|
||||
}
|
||||
|
||||
if (lightShaftsOn == true){
|
||||
|
||||
float rayValue = 0.0;
|
||||
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
int rayCount = rayPasses / (i + 1);
|
||||
rayValue = float(rayCount);
|
||||
// keep a raw non distorted samp for the noise to be filtereds
|
||||
float rayRaw = step(lightDepth[i], depth);
|
||||
|
||||
// ray part
|
||||
float rays = (1.0 - rayRaw);
|
||||
float rayFalloff = rayScale;
|
||||
for (int j = 0; j < rayCount; j++) {
|
||||
|
||||
float raySamp = step(lightDepth[i], texture2D(depthTex, uv + lightVec[i].xy * lightDepth[i] * rayFalloff).r);
|
||||
rays += (1.0 - raySamp) * (float(rayCount - j) / rayValue);
|
||||
rayFalloff += rayScale;
|
||||
|
||||
}
|
||||
rays /= rayValue;
|
||||
rays *= rayIntensity;
|
||||
|
||||
// radial noise part
|
||||
vec2 radialPointCoords = uv - lightPos[i].xy;
|
||||
vec2 radialNoiseCoords = vec2(128.0 * abs(vec2(atan(radialPointCoords.x, radialPointCoords.y), atan(radialPointCoords.x + radialPointCoords.y, -1.0 * radialPointCoords.x + radialPointCoords.y)) / tau));
|
||||
|
||||
float radialNoise = noise(radialNoiseCoords + camDirection[i].xy * 8.0 + radialAnimation[i] * 96.0) * rayRaw;
|
||||
|
||||
float positionFade = square(clamp(lightVec[i].z, 0.0, 1.0));
|
||||
radialNoise *= 0.25 * falloff[i];
|
||||
rays = (1.0 - rays) * falloff[i];
|
||||
vec3 rayColor = (redShift(linearFalloff(radialPointCoords * aspectRatio) + lightColor[i].b + 0.35 + radialNoise) * 0.25 + 0.75) * lightColor[i];
|
||||
color.rgb += rays * rayColor * (1.0 + radialNoise) * positionFade;
|
||||
}
|
||||
}
|
||||
|
||||
if (vignetteOn == true){
|
||||
color.rgb = vignette(color.rgb, radialFade);
|
||||
}
|
||||
|
||||
if (filmGrain == true){
|
||||
float t = fract(cycle);
|
||||
color.rgb += (rand(uv + 0.07 * t) + rand(uv + 0.11 * t) - 1.0) * 0.0078125 * filmGrainIntensity;
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(color.rgb, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
const float pi = 3.14159265358;
|
||||
const int nLightCount = 2;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
|
||||
uniform sampler2D depthTex;
|
||||
uniform vec2 lightPos[nLightCount];
|
||||
uniform vec2 texSize;
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform float cycle;
|
||||
|
||||
varying vec2 uv;
|
||||
|
||||
varying float lightDepth[nLightCount], fallOffDepth[nLightCount];
|
||||
varying vec4 lightVec[nLightCount], pos, camDirection[nLightCount];
|
||||
varying vec2 radialAnimation[nLightCount];
|
||||
varying vec3 lightColor[nLightCount];
|
||||
varying vec2 aspectRatio;
|
||||
|
||||
vec3 toLinear(vec3 color){
|
||||
return pow(color, vec3(2.2));
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
pos = vec4(vec2(2.0*(uv-0.5)),0.0,1.0);
|
||||
aspectRatio = vec2(texSize.x / texSize.y, 1.0);
|
||||
// in case someone plays it on a screen that is rotated 90
|
||||
if (texSize.x < texSize.y)
|
||||
aspectRatio = vec2(1.0, texSize.y / texSize.x);
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
// to not distort the rays, it needs the real uv's
|
||||
camDirection[i] = gl_ProjectionMatrix * vec4(lightPos[i], 0.0,1.0);
|
||||
fallOffDepth[i] = distance(gl_ProjectionMatrix * gl_LightSource[i].position - pos, pos);
|
||||
lightDepth[i] = (gl_ProjectionMatrixInverse * (normalize(gl_LightSource[i].position) - pos) * vec4(aspectRatio, 1.0, 1.0)).z;
|
||||
// we need to aspect correct to get a proper fade gradient
|
||||
lightVec[i] = (gl_ProjectionMatrix * normalize(gl_LightSource[i].position) - pos);
|
||||
// radial noise test // move to vertShaders
|
||||
radialAnimation[i] = vec2(abs(cycle - 0.5));
|
||||
radialAnimation[i].y = 1.0 - radialAnimation[i].x;
|
||||
lightColor[i] = toLinear(gl_LightSource[i].diffuse.rgb);
|
||||
}
|
||||
gl_Position = pos;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#version 130
|
||||
uniform sampler2D screen;
|
||||
uniform float time;
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 result;
|
||||
|
||||
const float strength = 0.22;
|
||||
const float two_pi = 6.282;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 sample = texture2D(screen, uv * 2.0);
|
||||
|
||||
result = sample;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
gl_Position = vec4(2.0*(uv.x-0.5), 2.0*(uv.y-0.5),0.0,1.0);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#define pi 3.141592653589793238462643383279
|
||||
|
||||
const float gridWidth = 120.0;
|
||||
const float gridSize = 25000.0;
|
||||
const float gridFadeDist = 300000000.0;
|
||||
|
||||
const float subGridWidth = 0.0;
|
||||
const float subGridSize = 50.0;
|
||||
const float subGridFadeDist = 0.0;
|
||||
|
||||
const float subsubGridWidth = 0.0;
|
||||
const float subsubGridSize = 50.0;
|
||||
const float subsubGridFadeDist = 0.0;
|
||||
|
||||
uniform float radius;
|
||||
uniform float planeDist;
|
||||
|
||||
varying vec2 uv;
|
||||
varying vec2 rcoords;
|
||||
|
||||
vec4 grid(float distance, float bordMult) {
|
||||
if(distance >= 1.0)
|
||||
return vec4(0, 0, 0, 0);
|
||||
|
||||
vec4 res;
|
||||
res.rgb = gl_Color.rgb;
|
||||
float baseAlpha = smoothstep(100.0, 200.0, planeDist) * 0.08;
|
||||
|
||||
vec2 absCoords = rcoords * radius;
|
||||
vec2 gridPos = abs(mod(absCoords, gridSize));
|
||||
gridPos = min(gridPos, gridSize - gridPos);
|
||||
|
||||
// Main grid alpha
|
||||
if(planeDist < gridFadeDist) {
|
||||
if(gridPos.x <= gridWidth * bordMult || gridPos.y <= gridWidth * bordMult) {
|
||||
res.a = baseAlpha * 2.0;
|
||||
return res;
|
||||
}
|
||||
|
||||
if(planeDist < subGridFadeDist) {
|
||||
vec2 subGridPos = abs(mod(absCoords, subGridSize));
|
||||
subGridPos = min(subGridPos, subGridSize - subGridPos);
|
||||
if(subGridPos.x <= subGridWidth * bordMult || subGridPos.y <= subGridWidth * bordMult) {
|
||||
res.a = baseAlpha * 1.8;
|
||||
return res;
|
||||
}
|
||||
|
||||
if(planeDist < subsubGridFadeDist) {
|
||||
vec2 subsubGridPos = abs(mod(absCoords, subsubGridSize));
|
||||
subsubGridPos = min(subsubGridPos, subsubGridSize - subsubGridPos);
|
||||
if(subsubGridPos.x <= subsubGridWidth * bordMult || subsubGridPos.y <= subsubGridWidth * bordMult) {
|
||||
res.a = baseAlpha * 1.5;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.a = 0.0;
|
||||
return res;
|
||||
}
|
||||
|
||||
void main() {
|
||||
float distance = length(rcoords);
|
||||
float bordMult = 1.0;
|
||||
if(distance > 0.9)
|
||||
discard;
|
||||
|
||||
vec4 color;
|
||||
if(uv.x > 0.5 && abs(uv.y - 0.5) < 0.002)
|
||||
color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
else
|
||||
color = grid(distance, bordMult);
|
||||
|
||||
color.a *= clamp((planeDist - 32000.0) / 60000.0, 0.0, 1.0);
|
||||
color.a *= clamp(1.0 - (planeDist - 300000.0) / 60000.0, 0.0, 1.0);
|
||||
|
||||
if (color.a == 0.0)
|
||||
discard;
|
||||
gl_FragColor = color;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
|
||||
//Masks: R = Glow, G = Owner Color, B = Engine Glow
|
||||
uniform sampler2D normalMap;
|
||||
uniform vec4 ownerColor;
|
||||
uniform vec3 glowColor;
|
||||
uniform float thrust;
|
||||
uniform mat3 invView;
|
||||
uniform float camDist;
|
||||
uniform float nodeScale;
|
||||
|
||||
varying vec3 forward, right;
|
||||
varying vec3 normal, binormal, tangent;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
vec2 skyboxSample(vec3 dir) {
|
||||
float x = dot(dir, forward);
|
||||
float z = dot(dir, right);
|
||||
float y = dot(dir, cross(forward,right));
|
||||
|
||||
return vec2(atan(-x,z) / 6.28318530718, (y + 1.0) * 0.5);
|
||||
}
|
||||
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
if(temp < 6600.0)
|
||||
c.g = mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0));
|
||||
else
|
||||
c.g = mix(0.976, 0.75, mixRange(temp, 6600.0, 29800.0));
|
||||
return c;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 normMap = (texture2D(normalMap, uv.xy).xyz * 2.0) - vec3(1.0);
|
||||
float ao = (length(normMap) - 0.5) * 2.0;
|
||||
|
||||
vec3 n = normalize(normal) * normMap.z;
|
||||
n += normalize(binormal) * normMap.y;
|
||||
n += normalize(tangent) * normMap.x;
|
||||
n = normalize(n);
|
||||
|
||||
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0 - dot(normalize(npos),n));
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#version 120
|
||||
uniform sampler2D texture;
|
||||
uniform vec2 texSize;
|
||||
//Margin setting in skin (pixels)
|
||||
uniform vec4 margin_src;
|
||||
uniform vec4 margin_dest;
|
||||
//Position and Size of region being rendered on the texture (pixels)
|
||||
uniform vec2 pos, size;
|
||||
//Size of region being rendered to (pixels)
|
||||
uniform vec2 size_out;
|
||||
//Mode of rendering (0=Uniform, 1=Scaled, 2=Tiled)
|
||||
uniform vec2 dim_mode;
|
||||
|
||||
#define MAX_GRADIENTS 8
|
||||
//Number of gradients used
|
||||
uniform float gradientCount;
|
||||
//Gradient location (in pixels) relative to the rendered destination
|
||||
uniform vec4[MAX_GRADIENTS] gradientRects;
|
||||
//Gradient colors (tl, tr, bl, br)
|
||||
uniform vec4[MAX_GRADIENTS*4] gradientColors;
|
||||
//Gradient mode
|
||||
uniform float gradientMode;
|
||||
|
||||
varying vec4 color;
|
||||
varying vec2 uv;
|
||||
varying vec2 qpos;
|
||||
|
||||
float realPos(int dimension, float point) {
|
||||
if(dim_mode[dimension] == 0.0) //Uniform
|
||||
return pos[dimension] + (point * size[dimension]);
|
||||
else {
|
||||
//Margin expressed in 0-1 for the destination region
|
||||
float l_margin = margin_dest[dimension] / size_out[dimension];
|
||||
float r_margin = margin_dest[dimension+2] / size_out[dimension];
|
||||
if(point <= l_margin)
|
||||
return pos[dimension] + (point / l_margin * margin_src[dimension]);
|
||||
else if(point >= 1.0 - r_margin)
|
||||
return pos[dimension] + size[dimension] + (point-1.0) / r_margin * margin_src[dimension+2];
|
||||
|
||||
float region_inner_size = size_out[dimension] - margin_dest[dimension] - margin_dest[dimension+2];
|
||||
float inner_size = size[dimension] - margin_src[dimension] - margin_src[dimension+2];
|
||||
|
||||
//point is now 0-1 through the inner destination region
|
||||
point = (point - l_margin) * (size_out[dimension] / region_inner_size);
|
||||
|
||||
if(dim_mode[dimension] == 1.0) { //Scaled
|
||||
return pos[dimension] + margin_src[dimension] + (point * inner_size);
|
||||
}
|
||||
else { //Tiled
|
||||
point = fract(point * region_inner_size / inner_size);
|
||||
return pos[dimension] + margin_src[dimension] + (point * inner_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 tcoord =
|
||||
vec2( realPos(0, uv.x) / float(texSize.x),
|
||||
realPos(1, uv.y) / float(texSize.y) );
|
||||
|
||||
vec4 skinSample = texture2D(texture, tcoord, -4);
|
||||
|
||||
if(skinSample.a < 0.01 && gradientMode == 0.0)
|
||||
discard;
|
||||
|
||||
//Apply gradients
|
||||
for(int i = 0; i < int(min(float(gradientCount), float(MAX_GRADIENTS))); ++i) {
|
||||
vec4 rect = gradientRects[i] / size_out.xyxy;
|
||||
|
||||
vec2 gPos =
|
||||
vec2( (uv.x - rect.x) / (rect.z - rect.x),
|
||||
(uv.y - rect.y) / (rect.w - rect.y) );
|
||||
|
||||
//Make sure the tranformed rect is within (0,0) (1,1)
|
||||
if( all( bvec4(greaterThanEqual(gPos, vec2(0.0,0.0)), lessThanEqual(gPos, vec2(1.0,1.0))) ) ) {
|
||||
//Add noise
|
||||
float rnd = dot(qpos, pos);
|
||||
gPos.y = clamp(gPos.y + mod(rnd/max(mod(uv.x,0.01)/0.01, 0.001), 0.1)-0.05, 0.0, 1.0);
|
||||
gPos.x = clamp(gPos.x + mod(rnd/max(mod(uv.y,0.01)/0.01, 0.001), 0.1)-0.05, 0.0, 1.0);
|
||||
|
||||
//Bilinear interpolation for the gradient color
|
||||
vec4 gCol = mix(
|
||||
mix(gradientColors[i*4], gradientColors[i*4+1], gPos.x),
|
||||
mix(gradientColors[i*4+2], gradientColors[i*4+3], gPos.x),
|
||||
gPos.y );
|
||||
|
||||
if(gradientMode == 1.0)
|
||||
skinSample = gCol;
|
||||
else
|
||||
skinSample.rgb = mix(skinSample.rgb, gCol.rgb, gCol.a);
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = skinSample * color;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#version 120
|
||||
uniform sampler2D texture;
|
||||
uniform vec2 texSize;
|
||||
//Margin setting in skin (pixels)
|
||||
uniform vec4 margin_src;
|
||||
uniform vec4 margin_dest;
|
||||
//Position and Size of region being rendered on the texture (pixels)
|
||||
uniform vec2 pos, size;
|
||||
//Size of region being rendered to (pixels)
|
||||
uniform vec2 size_out;
|
||||
//Mode of rendering (0=Uniform, 1=Scaled, 2=Tiled)
|
||||
uniform vec2 dim_mode;
|
||||
|
||||
//Number of gradients used
|
||||
uniform float gradientCount;
|
||||
//Gradient location (in pixels) relative to the rendered destination
|
||||
uniform vec4 gradientRect;
|
||||
//Gradient colors (tl, tr, bl, br)
|
||||
uniform vec4[4] gradientColors;
|
||||
//Gradient mode
|
||||
uniform float gradientMode;
|
||||
|
||||
varying vec4 color;
|
||||
varying vec2 uv;
|
||||
varying vec2 qpos;
|
||||
|
||||
float realPos(int dimension, float point) {
|
||||
if(dim_mode[dimension] == 0.0) //Uniform
|
||||
return pos[dimension] + (point * size[dimension]);
|
||||
else {
|
||||
//Margin expressed in 0-1 for the destination region
|
||||
float l_margin = margin_dest[dimension] / size_out[dimension];
|
||||
float r_margin = margin_dest[dimension+2] / size_out[dimension];
|
||||
if(point <= l_margin)
|
||||
return pos[dimension] + (point / l_margin * margin_src[dimension]);
|
||||
else if(point >= 1.0 - r_margin)
|
||||
return pos[dimension] + size[dimension] + (point-1.0) / r_margin * margin_src[dimension+2];
|
||||
|
||||
float region_inner_size = size_out[dimension] - margin_dest[dimension] - margin_dest[dimension+2];
|
||||
float inner_size = size[dimension] - margin_src[dimension] - margin_src[dimension+2];
|
||||
|
||||
//point is now 0-1 through the inner destination region
|
||||
point = (point - l_margin) * (size_out[dimension] / region_inner_size);
|
||||
|
||||
if(dim_mode[dimension] == 1.0) { //Scaled
|
||||
return pos[dimension] + margin_src[dimension] + (point * inner_size);
|
||||
}
|
||||
else { //Tiled
|
||||
point = fract(point * region_inner_size / inner_size);
|
||||
return pos[dimension] + margin_src[dimension] + (point * inner_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 tcoord =
|
||||
vec2( realPos(0, uv.x) / float(texSize.x),
|
||||
realPos(1, uv.y) / float(texSize.y) );
|
||||
|
||||
vec4 skinSample = texture2D(texture, tcoord, -4);
|
||||
|
||||
if(skinSample.a < 0.01 && gradientMode == 0.0)
|
||||
discard;
|
||||
|
||||
//Apply gradients
|
||||
if(gradientCount != 0.0) {
|
||||
vec4 rect = gradientRect / size_out.xyxy;
|
||||
|
||||
vec2 gPos =
|
||||
vec2( (uv.x - rect.x) / (rect.z - rect.x),
|
||||
(uv.y - rect.y) / (rect.w - rect.y) );
|
||||
|
||||
//Make sure the tranformed rect is within (0,0) (1,1)
|
||||
if( all( bvec4(greaterThanEqual(gPos, vec2(0.0,0.0)), lessThanEqual(gPos, vec2(1.0,1.0))) ) ) {
|
||||
//Add noise
|
||||
float rnd = dot(qpos, pos);
|
||||
gPos.y = clamp(gPos.y + mod(rnd/max(mod(uv.x,0.01)/0.01, 0.001), 0.1)-0.05, 0.0, 1.0);
|
||||
gPos.x = clamp(gPos.x + mod(rnd/max(mod(uv.y,0.01)/0.01, 0.001), 0.1)-0.05, 0.0, 1.0);
|
||||
|
||||
//Bilinear interpolation for the gradient color
|
||||
vec4 gCol = mix(
|
||||
mix(gradientColors[0], gradientColors[1], gPos.x),
|
||||
mix(gradientColors[2], gradientColors[3], gPos.x),
|
||||
gPos.y );
|
||||
|
||||
if(gradientMode == 1.0)
|
||||
skinSample = gCol;
|
||||
else
|
||||
skinSample.rgb = mix(skinSample.rgb, gCol.rgb, gCol.a);
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = skinSample * color;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#version 120
|
||||
varying vec4 color;
|
||||
varying vec2 uv;
|
||||
varying vec2 qpos;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = gl_Color;
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
gl_Position = ftransform();
|
||||
qpos = gl_Position.xy;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
if(abs(uv.x - 0.5) > 0.25)
|
||||
if(abs(abs(uv.x - 0.5)-0.25) * 2.0 > 0.5 - abs(uv.y - 0.5))
|
||||
discard;
|
||||
gl_FragColor = gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = gl_MultiTexCoord0.xy;
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
uniform float value;
|
||||
uniform float minSat;
|
||||
uniform float maxSat;
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
float chroma = (1.0 - uv.y) * value * (maxSat - minSat) + minSat;
|
||||
float X = chroma * (1.0 - (abs(mod(uv.x * 6.0, 2.0) - 1.0)));
|
||||
float m = value - chroma;
|
||||
|
||||
chroma += m;
|
||||
X += m;
|
||||
|
||||
vec3 rgb = vec3(chroma);
|
||||
vec2 lower = vec2(X, m);
|
||||
|
||||
float segment = floor(uv.x * 6.0);
|
||||
if(segment == 0.0)
|
||||
rgb.gb = lower;
|
||||
else if(segment == 1.0)
|
||||
rgb.rb = lower;
|
||||
else if(segment == 2.0)
|
||||
rgb.br = lower;
|
||||
else if(segment == 3.0)
|
||||
rgb.gr = lower;
|
||||
else if(segment == 4.0)
|
||||
rgb.rg = lower;
|
||||
else// if(segment == 5.0)
|
||||
rgb.bg = lower;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
uniform float factor;
|
||||
|
||||
void main() {
|
||||
vec4 sample = texture2D(texture,uv);
|
||||
gl_FragColor = vec4(mix(sample.rgb, gl_Color.rgb, factor), gl_Color.a * sample.a);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
uniform sampler2D mask;
|
||||
|
||||
void main() {
|
||||
vec4 sample = texture2D(texture,uv);
|
||||
gl_FragColor = mix(sample, sample * gl_Color, texture2D(mask,uv).a);
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530717;
|
||||
uniform sampler2D biomes, cities, differenceNoise, lookup, cityGlow, /*testSplat, */surfaceData;
|
||||
uniform samplerCube skybox;
|
||||
uniform float lightRadius[2];
|
||||
uniform vec4 ownerColor;
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsPrimary = ownerColor.rgb * 0.85 + 0.15;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.brg) * 0.85 + 0.15;
|
||||
vec3 colorLightsTertiary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
////Amount of population considered 'half full'
|
||||
//const float basePopulation = 8.0;
|
||||
vec2 uvClamps = vec2(1.0, 1.0);
|
||||
|
||||
varying vec2 uv, uv2, uv3;
|
||||
|
||||
varying vec3 normal, v, vertCol, origo;
|
||||
varying vec4 uvNoise;
|
||||
varying float pulse;
|
||||
varying mat3 tcamrot;
|
||||
/*
|
||||
black biome is base biome, its biome color picks are also what controls the ocean color picks
|
||||
red biome is secondary biome, that will splat on top of base
|
||||
green biome is third biome, that will splat on top, the poles are hardcoded to be green biome, so keep it the coldest biome pick wise.
|
||||
ocean and cracks a bonus biomes, so with both there can be 5 i total. Blue > 0.5 is ocean, blue < 0.5 is cracks. Ocean is using the combined height for detail, cracks is using ao to spawn in cracks.
|
||||
alpha is city location and density, it rules over all others but will be build under water.
|
||||
*/
|
||||
|
||||
//vec2(0.0, 0.0) vulcanic
|
||||
//vec2(0.25, 0.0) crystal
|
||||
//vec2(0.5, 0.0) mountains
|
||||
//vec2(0.75, 0.0) cracked
|
||||
//vec2(0.0, 0.5) ice
|
||||
//vec2(0.25, 0.5) barren
|
||||
//vec2(0.5, 0.5) terran
|
||||
//vec2(0.75, 0.5) desert
|
||||
|
||||
// x = 0-1 color wheel picker, y is intensity
|
||||
uniform vec4 cracksColorIntensityBiomeSeed;
|
||||
uniform vec4 biomeOffsets;
|
||||
|
||||
varying vec2 polarGradients;
|
||||
varying vec3 light[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
|
||||
|
||||
// /*varying */vec4 biomeOffsets = vec4(0.75, 0.0, 0.25, 0.5);
|
||||
// /*varying */float biomePicks = (1.0 / 2048.0) * 470.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.002, 0.001);
|
||||
const int pStep = 0;
|
||||
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = sqrt(abs(1.0 - n.x * n.x - n.y * n.y));
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 paletteBlackBody( float t )
|
||||
{
|
||||
t *= t;
|
||||
return square(min(vec3(1.0), 0.45 + 0.35*cos( tau*((t * 0.9)+vec3(0.45, 0.55, 0.65)) ) + (1.0 - t) * 0.25));
|
||||
}
|
||||
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow8(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x*x;
|
||||
}
|
||||
|
||||
// speculer term part 1, GGX.
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float denominator = HdotN * HdotN * (Roughness - 1.0) + 1.0;
|
||||
return Roughness /( pi * denominator * denominator);
|
||||
}
|
||||
// specular term part 2, GGX.
|
||||
float V_SchlickforGGX(float Roughness, float G1V, float NdotL)
|
||||
{
|
||||
float G1L = NdotL * (1.0 - Roughness) + Roughness;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow5(1.0 - max(0.0,(dot(l, h)))));
|
||||
}
|
||||
// fresnel for ambient light for linear GGX.
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow5((1.0 - dotProduct)) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term, fake fast approximation
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0 - NdotL * clamp(1.0-NdotV / 2.0, 0.0,1.0), pi) * 0.62;
|
||||
return mix(O, NdotL, roughness);
|
||||
}
|
||||
// note feed it inverted roughness squared
|
||||
float sphereLightEnergyConservation(vec3 toLightCenter, float radius, float roughness)
|
||||
{
|
||||
float invDistToLight = inversesqrt(dot(toLightCenter, toLightCenter));
|
||||
float sphereAngle = max(0.0, radius * invDistToLight);
|
||||
return square(roughness / max(0.0, roughness + 0.5 * sphereAngle));
|
||||
}
|
||||
|
||||
vec3 sphereLightClosestPoint(vec3 l,vec3 r,float radius)
|
||||
{
|
||||
vec3 centerToRay = dot(l, r) * r - l;
|
||||
return l + centerToRay * clamp(radius / length(centerToRay), 0.0, 1.0); // just max will make donut shaped highlights:oP
|
||||
}
|
||||
|
||||
// cosine based palette, 4 vec3 params
|
||||
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d )
|
||||
{
|
||||
return a + b*cos( 6.28318*(c*t+d) );
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
vec3 test = vec3(0.0);
|
||||
float splatMapNoise = noise3D(origo * 0.5+ origo * vertCol.g);
|
||||
|
||||
float normalIntensity = 1.0;
|
||||
|
||||
vec4 uvS = vec4(uv, uv2);
|
||||
|
||||
// because we have two uvs, for poles and equator, to avoid polar distortions and hide tiling, we need two matrices for separated normal mapping
|
||||
mat3 TBNA;
|
||||
mat3 TBNB;
|
||||
|
||||
// Normal and tangent setup
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(uvS);
|
||||
vec4 duv2 = duv2Calc(uvS);
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBNA = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
tangent = dp2perp * duv1.z + dp1perp * duv2.z;
|
||||
binormal = dp2perp * duv1.w + dp1perp * duv2.w;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBNB = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
vec4 parSamp = vec4(texture2D(biomes, uvS.xy + biomeOffsets.xy).a,texture2D(biomes, uvS.xy + biomeOffsets.zw).a,texture2D(biomes, uvS.zw + biomeOffsets.xy).a,texture2D(biomes, uvS.zw + biomeOffsets.zw).a);
|
||||
vec2 splatMix = min(vec2(1.0), vec2((parSamp.r + parSamp.g + splatMapNoise), (parSamp.b + parSamp.a + splatMapNoise)) * splatMapNoise);
|
||||
|
||||
// create combined detail texture and calculate weighted splat mapping for the two uv sets
|
||||
parSamp.rg = vec2(mix(parSamp.r, parSamp.g, splatMix.r), mix(parSamp.b, parSamp.a, splatMix.g));
|
||||
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
|
||||
// do parallax
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec4 vProjVTex = (duv1 * vProjVScr.x + duv2 * vProjVScr.y);
|
||||
vec2 vProjVTexZ = mix(vec2(0.0), (vec2(parSamp.r, parSamp.g) * scaleBias.r - scaleBias.g), NdotV);
|
||||
|
||||
uvS += (vProjVTex * vProjVTexZ.xxyy);
|
||||
|
||||
|
||||
// sample all four biomes once for each of the first two uv sets
|
||||
vec4 firstbiomeA = texture2D(biomes, uvS.xy + biomeOffsets.xy);
|
||||
vec4 firstBiomeB = texture2D(biomes, uvS.zw + biomeOffsets.xy);
|
||||
vec4 secondbiomeA = texture2D(biomes, uvS.xy + biomeOffsets.zw);
|
||||
vec4 secondBiomeB = texture2D(biomes, uvS.zw + biomeOffsets.zw);
|
||||
// mix the normals cavity and resampled heightmap
|
||||
vec4 dataSampA = mix(firstbiomeA, secondbiomeA, splatMix.r);
|
||||
vec4 dataSampB = mix(firstBiomeB, secondBiomeB, splatMix.g);
|
||||
|
||||
// create combined detail splatmask to mix the first two uv sets
|
||||
float uvBlend = min(1.0, (dataSampA.a + dataSampB.a + vertCol.b) * vertCol.b);
|
||||
|
||||
// perform TBN matrix multiplication for each of the two normal maps separately and mix, save instructions by mix in height in alpha.
|
||||
vec4 n = vec4(dataSampA.xy, dataSampB.xy);
|
||||
n *= 2.0;
|
||||
n -=1.0;
|
||||
n = mix(vec4(normalize(TBNA * normalize(vec3(n.xy, deriveZ(n.xy)))), (dataSampA.a + dataSampA.b) * 0.5), vec4(normalize(TBNB * normalize(vec3(n.zw, deriveZ(n.zw)))), (dataSampB.a + dataSampB.b) * 0.5), uvBlend);
|
||||
// move these out of the mixing later
|
||||
vec3 r = normalize(reflect(-v, n.xyz));
|
||||
NdotV = max(0.0, dot(n.xyz, v));
|
||||
|
||||
// sample albedo and roughness from lookup table
|
||||
vec4 albedo = texture2D(lookup, vec2(cracksColorIntensityBiomeSeed.z, n.a)); //, 0.0
|
||||
albedo.rgb = toLinear(albedo.rgb);
|
||||
float metalness = 0.001;//clamp(((1.0 - dot(albedoR.xz, albedoR.xz)) - albedoR.y) * 2.0, 0.0, 1.0);// * waterMask;
|
||||
vec3 substance = (0.04 - 0.04 * metalness) + albedo.rgb * metalness;// * clamp(2048.0 - 1780.0, 0.0,1.0);
|
||||
albedo.rgb -= substance;
|
||||
float ao = square(min(1.0,(n.a * 2.0)));
|
||||
|
||||
vec3 oceanColor = texture2D(lookup, vec2(cracksColorIntensityBiomeSeed.z, max(0.995, 0.05))).rgb; // from 0.0065 to 0.0025
|
||||
|
||||
// clamp to keep in PBR safe ranges - nothing in reality either albedo or roughness is 0 or 1 and can make the math fail
|
||||
albedo = clamp(albedo, vec4(0.05), vec4(0.95));
|
||||
|
||||
|
||||
// create sss mask
|
||||
vec4 invertedAlbedo = 1.0 - albedo;
|
||||
float SSSmask = mix(NdotV * 0.5 + 0.5, 1.0 - min(1.0, pow8((1.0 - invertedAlbedo.a * invertedAlbedo.b) * (1.0 - invertedAlbedo.r * invertedAlbedo.g))) * n.a, n.a);
|
||||
|
||||
|
||||
//cracksColorIntensityBiomeSeed
|
||||
|
||||
albedo.a = pow(albedo.a, 0.5);
|
||||
// ambient reflections
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,albedo.a);
|
||||
vec3 color = square((textureCube(skybox, r, sqrt(albedo.a) * 8.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 8.0).rgb + 0.024) * albedo.rgb * (1.0 - ambientFresnel);
|
||||
|
||||
// pbr calculations that would be done twice in loop
|
||||
vec3 roughness = vec3(square(vec2(albedo.a, albedo.a * 0.5 + 0.5)), 0.0);
|
||||
roughness.r = square(roughness.r);
|
||||
roughness.g *=0.5;
|
||||
roughness.b = 1.0 - square(albedo.a);
|
||||
float G1V = NdotV * (1.0 - roughness.g) + roughness.g;
|
||||
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
vec3 L = sphereLightClosestPoint((light[i]), r , lightRadius[i]);
|
||||
|
||||
float energy = (sphereLightEnergyConservation(L, lightRadius[i], roughness.b));
|
||||
L = normalize(L);
|
||||
vec2 NdotL = max(vec2(0.0), vec2(dot(mix(n.xyz, normal, 0.5),L) * 0.8 + 0.2, dot(n.xyz,L)));
|
||||
|
||||
float attenuation = 1.0f / (1.0f + dot(L ,L) / lightRadius[i]) * NdotL.x;
|
||||
//citylights *= 1.0 - attenuation;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v * 0.5;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n.xyz));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, roughness.r));
|
||||
float V = max(0.0, V_SchlickforGGX(roughness.g, G1V, NdotL.y));
|
||||
float O = OrenNayerforDiffuseOnly(albedo.a, NdotL.y, NdotV);
|
||||
|
||||
|
||||
// sub surface scattering model
|
||||
float inScatter = pow(clamp(dot(L, -v), 0.0, 1.0), 12.0) * mix(8.0, 0.1, SSSmask);
|
||||
float normalContribution = clamp(dot(mix(normal, n.xyz, SSSmask), halfVec) * SSSmask + 1.0 - SSSmask, 0.0, 1.0);
|
||||
float backScatter = n.a * normalContribution / tau;
|
||||
vec3 SSS = mix(backScatter, 1.0, inScatter) * square(oceanColor) * NdotL.x;
|
||||
SSS = vec3(0.0);
|
||||
|
||||
color += ((D * V * F * energy) + ((1.0 - F) * O * albedo.rgb) + SSS) * toLinear(gl_LightSource[i].diffuse.rgb) * attenuation;
|
||||
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= n.a * 0.5 + 1.0;
|
||||
|
||||
gl_FragColor.rgb = toGamma(vec3(color));
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
uniform float time;
|
||||
//uniform vec3 wsPos;
|
||||
uniform vec4 wsRot;
|
||||
|
||||
varying vec4 uvNoise;
|
||||
varying vec3 normal, v, vertCol;
|
||||
varying vec2 uv, uv2, uv3, uvB[4];
|
||||
varying float pulse;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 origo;
|
||||
|
||||
const vec2 fractions = vec2(0.984375, 0.0078125);
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 wsAllign(vec3 x){
|
||||
return x + 2.0 * cross(wsRot.xyz, cross(wsRot.xyz, x) + wsRot.w * x);
|
||||
}
|
||||
|
||||
vec2 invertBlurEdge(vec2 uvB){
|
||||
return abs(fract((uvB + 1.0) * 0.5) - 0.5) * 2.0;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
origo = in_vertex.xyz;
|
||||
pulse = abs(time * 2.0 - 1.0);
|
||||
vec4 pos = gl_ModelViewMatrix * in_vertex;
|
||||
|
||||
// convert view, normal and light vectors to world space and quaternion correct for model rotation
|
||||
mat3 tcamrot = transpose(mat3x3(gl_ModelViewMatrix));
|
||||
v = normalize(wsAllign(normalize(tcamrot * -pos.xyz)));
|
||||
|
||||
// special view vector to correct just for cubemap reflections
|
||||
normal = normalize(tcamrot * (gl_NormalMatrix * wsAllign(normalize(in_normal))));
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = wsAllign(((tcamrot * (((gl_LightSource[i].position)).xyz - pos.xyz))));
|
||||
}
|
||||
|
||||
uv = in_uv;//gridUV.xy * 3.0; // still 1/3rd to compensate for vert precision
|
||||
uv2 = in_uv2.xy;
|
||||
uv3 = in_uv2.zw;
|
||||
|
||||
uv3.x = 1.0 - uv3.x;
|
||||
|
||||
vertCol = in_color.rgb;
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
vec4 col = gl_Color;
|
||||
col.a = (1.0 - abs(0.5 - uv.y)) * 2.0;
|
||||
gl_FragColor = col;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
uniform float age, time;
|
||||
varying vec3 n, v, norm;
|
||||
|
||||
vec3 rangeUnpack(float t) {
|
||||
vec3 r;
|
||||
r.x = abs(mod(t + 0.33333,1.0) - 0.5) / 0.5;
|
||||
r.y = abs(mod(t + 0.66666,1.0) - 0.5) / 0.5;
|
||||
r.z = abs(mod(t + 1.0,1.0) - 0.5) / 0.5;
|
||||
return r;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 sample = texture2D(texture,uv).rgb;
|
||||
sample = sample * rangeUnpack(mod(age * 15.0, 1.0));
|
||||
float alpha = max(sample.x + sample.y + sample.z - 0.7, 0.0);
|
||||
alpha *= max(dot(normalize(norm), normalize(-v)), 0.0) * (1.0 - abs(n.y)) * (1.0 - age);
|
||||
alpha *= 20.0;
|
||||
if(alpha <= 0.0)
|
||||
discard;
|
||||
|
||||
gl_FragColor.rgb = alpha * vec3(1.0, 0.7, 0.2);
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
attribute vec3 in_position;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec3 in_normal;
|
||||
varying vec3 n, v, norm;
|
||||
varying vec2 uv;
|
||||
|
||||
void main() {
|
||||
uv = in_uv;
|
||||
n = in_normal;
|
||||
norm = gl_NormalMatrix * in_normal;
|
||||
vec4 vert = ftransform();
|
||||
gl_Position = vert;
|
||||
v = vert.xyz;
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
#version 120
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool sss = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float lightIntensity = 2.0;
|
||||
|
||||
|
||||
uniform sampler2D model, detail;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec3 colors[nLightCount];
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform vec4 ownerColor;
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.7 + 0.3;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.7 + 0.3;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.7 + 0.3;
|
||||
|
||||
varying float vertMask;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv, uv2;
|
||||
varying vec4 pos;
|
||||
|
||||
uniform float mineBuild;
|
||||
|
||||
varying vec3 light[nLightCount];
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const float rayScale = 0.001;
|
||||
const int rSteps = 5;
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec4 square(vec4 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
float deriveZ(vec2 n) {
|
||||
return sqrt(abs(1.0 - n.x * n.x - n.y * n.y));
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p) {
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p) {
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv) {
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv) {
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 uvR = vec4(uv, uv2);
|
||||
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
vec3 albedo = vec3(0.38, 0.2, 0.58);
|
||||
vec3 substance = vec3(0.0); // essentially an rgb specular color extracted from the albedo through metalness
|
||||
float metalness = 0.0; // dielectric or metallic surface
|
||||
float orgRoughness = 0.0; // specular/reflection sharpness
|
||||
float cavity = 0.5; // hard multiplier
|
||||
|
||||
mat3 TBNA = mat3(0.0);
|
||||
mat3 TBNB = mat3(0.0);
|
||||
|
||||
// results
|
||||
vec3 color = vec3(0.0);
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
vec4 surfSampB = texture2D(model, uvR.zw);
|
||||
vec4 subSampB = texture2D(detail, uvR.zw * vec2(1.0, 6.0));
|
||||
vec4 surfSampA = texture2D(model, uvR.xy);
|
||||
vec4 subSampA = texture2D(detail, uvR.xy * vec2(1.0, 6.0));
|
||||
vec3 NdotV = vec3(max(0.0, dot(n, v)), 0.0, 0.0);
|
||||
|
||||
if (normalMapping){
|
||||
// Normal and tangent setup
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(uvR);
|
||||
vec4 duv2 = duv2Calc(uvR);
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, n);
|
||||
vec3 dp1perp = cross(n, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBNA = mat3(tangent * invmax, binormal * invmax, n);
|
||||
|
||||
tangent = dp2perp * duv1.z + dp1perp * duv2.z;
|
||||
binormal = dp2perp * duv1.w + dp1perp * duv2.w;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBNB = mat3(tangent * invmax, binormal * invmax, n);
|
||||
|
||||
uvR *= vec4(1.0, 6.0, 1.0, 6.0);
|
||||
|
||||
if(parallax){
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec4 vProjVTex = (duv1 * vProjVScr.x + duv2 * vProjVScr.y);
|
||||
float p = 0.0;
|
||||
|
||||
for (int i = 0; i < rSteps; i++) {
|
||||
p -=(subSampA.a * rayScale);
|
||||
float vProjVTexZ = NdotV.x * p;
|
||||
uvR.xy += (vProjVTex.xy * vProjVTexZ);
|
||||
subSampA += texture2D(detail, uvR.xy, i);
|
||||
}
|
||||
subSampA /= float(rSteps) + 1.0;
|
||||
p = 0.0;
|
||||
|
||||
vec3 vB;
|
||||
for (int i = 0; i < rSteps; i++) {
|
||||
p -=(subSampB.a * rayScale);
|
||||
float vProjVTexZ = NdotV.x * p;
|
||||
uvR.zw += (vProjVTex.zw * vProjVTexZ);
|
||||
subSampB += texture2D(detail, uvR.zw, i);
|
||||
}
|
||||
subSampB /= float(rSteps) + 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
vec2 surfSamp = mix(surfSampA.zw, surfSampB.zw, vertMask);
|
||||
vec2 subSamp = mix(subSampA.zw, subSampB.zw, vertMask);
|
||||
vec4 nS = vec4(n, 0.0);
|
||||
vec4 nSS = nS;
|
||||
vec3 SSScolor = albedo;
|
||||
|
||||
if (normalMapping){
|
||||
nS = vec4(surfSampA.xy, surfSampB.xy);
|
||||
nS *= nS *(3.0 - 2.0 * nS);
|
||||
nS *= 2.0;
|
||||
nS -=1.0;
|
||||
nSS = vec4(subSampA.xy, subSampB.xy) * 2.0;
|
||||
nSS -=1.0;
|
||||
nSS *= 0.5;
|
||||
|
||||
nS.xyz = mix(normalize(TBNA * normalize(vec3(nS.xy, deriveZ(nS.xy)))), normalize(TBNB * normalize(vec3(nS.zw, deriveZ(nS.zw)))), vertMask);
|
||||
nSS.xyz = mix(normalize(TBNA * normalize(vec3(nSS.xy, deriveZ(nSS.xy)))), normalize(TBNB * normalize(vec3(nSS.zw, deriveZ(nSS.zw)))), vertMask);
|
||||
|
||||
albedo = toLinear(min(vec3(1.0), (albedo + albedo * surfSamp.g + subSamp.r * albedo + surfSamp.g * 0.25) * surfSamp.g));
|
||||
SSScolor = albedo + surfSamp.g * albedo;
|
||||
|
||||
orgRoughness = (1.0 - surfSamp.g) * 0.1 + 0.1;
|
||||
|
||||
NdotV.z = max(0.0, dot(nSS.xyz, v));
|
||||
|
||||
nSS.xyz = mix(nSS.xyz, nS.xyz, NdotV.z);
|
||||
}
|
||||
|
||||
vec3 r = normalize(reflect(-v, nS.xyz));
|
||||
|
||||
metalness = clamp(1.0 - surfSamp.g * surfSamp.r, 0.0, 1.0);
|
||||
substance = (0.04 - 0.04 * metalness) + albedo * metalness;
|
||||
albedo -= substance;
|
||||
|
||||
NdotV.xy = max(vec2(0.0), vec2(dot(nSS.xyz, v), dot(nS.xyz, v)));
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV.y ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, nS.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
vec3 test = vec3(0.0);
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
vec2 NdotL = max(vec2(0.0), vec2(dot(nSS.xyz, L) + 1.0, dot(nS.xyz, L)));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
// also note I never do the Oren-Nayer diffuse calculation
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL.y;
|
||||
|
||||
// note no attenuation check, to not kill the sss and to allow the spec to overshoot its tail
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotV = max(0.0, dot(halfVec, v));
|
||||
vec2 HdotN = max(vec2(0.0), vec2(dot(halfVec, nS.xyz), dot(halfVec, nSS.xyz)));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN.x, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV.y, NdotL.y));
|
||||
|
||||
float inscatter = pow(clamp(dot(L, -v), 0.0, 1.0), 12.0) * mix(3.0, .1, (1.0 - subSamp.g) * 0.5 + 0.5);
|
||||
float normalContribution = clamp(HdotN.y * subSamp.g + 1.0 - subSamp.r, 0.0, 1.0);
|
||||
float backscatter = surfSamp.r * (normalContribution / tau);
|
||||
float SSS = mix(backscatter, 1.0, inscatter) * NdotL.x;
|
||||
|
||||
color += (((F * V * D) + albedo * NdotL.y * (1.0 - F)) * attenuation + SSScolor * SSS) * gl_LightSource[i].diffuse.rgb;
|
||||
|
||||
}
|
||||
// note, no hard cavity multiplier
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
vec2 NdotL = max(vec2(0.0), vec2(dot(nSS.xyz, L) + 1.0, dot(nS.xyz, L)));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
// also note I never do the Oren-Nayer diffuse calculation
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL.y;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
vec2 HdotN = max(vec2(0.0), vec2(dot(halfVec, nS.xyz), dot(halfVec, nSS.xyz)));
|
||||
vec3 S = Fresnel2(substance, HdotN.x ,orgRoughness);
|
||||
|
||||
float inscatter = pow(clamp(dot(L, -v), 0.0, 1.0), 12.0) * mix(3.0, .1, (1.0 - subSamp.g) * 0.5 + 0.5);
|
||||
float normalContribution = clamp(HdotN.y * subSamp.g + 1.0 - subSamp.r, 0.0, 1.0);
|
||||
float backscatter = surfSamp.r * (normalContribution / tau);
|
||||
float SSS = mix(backscatter, 1.0, inscatter) * NdotL.x;
|
||||
|
||||
color += ((pow(S * HdotN.x, vec3(orgRoughness + 5.0)) + albedo * NdotL.y) * attenuation + SSScolor * SSS) * gl_LightSource[i].diffuse.rgb;
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0)));
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec4 in_color;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_uv2;
|
||||
uniform vec4 wsRot;
|
||||
|
||||
varying float vertMask;
|
||||
varying vec3 npos;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv, uv2;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec4 pos;
|
||||
|
||||
vec3 wsAllign(vec3 x){
|
||||
return x + 2.0 * cross(wsRot.xyz, cross(wsRot.xyz, x) + wsRot.w * x);
|
||||
}
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
pos = gl_ModelViewMatrix * in_vertex;
|
||||
|
||||
// convert view, normal and light vectors to world space and quaternion correct for model rotation
|
||||
mat3 tcamrot = transpose(mat3x3(gl_ModelViewMatrix));
|
||||
npos = (wsAllign(normalize(tcamrot * -pos.xyz)));
|
||||
|
||||
// special view vector to correct just for cubemap reflections
|
||||
normal = (tcamrot * (gl_NormalMatrix * wsAllign(normalize(in_normal))));
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = wsAllign(normalize((tcamrot * (((gl_LightSource[i].position)).xyz - pos.xyz))));
|
||||
}
|
||||
|
||||
vertMask = in_color.r;
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
|
||||
uniform sampler2D diffuseRGBspecA, cities;
|
||||
uniform float cycles[6];
|
||||
uniform float population;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
|
||||
const float waveHeight = 2.0;
|
||||
//Wave length must be an integer, or there will be hitches in the animation
|
||||
const float waveLength = 2.0;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
vec3 wave(vec3 epicenter, vec3 n, float t, float wl) {
|
||||
float ndot = 1.0 + dot(epicenter, n);
|
||||
float bend = cos((t + ndot * ndot) * 6.28318530718 * (waveLength + wl * 12.0)) * waveHeight / (1.0 + wl);
|
||||
vec3 toward = cross(cross(n,epicenter),n);
|
||||
return (toward * bend) + (n * (1.0 - abs(bend)));
|
||||
}
|
||||
|
||||
vec3 waveGroup_0(vec3 n) {
|
||||
vec3 nrm = vec3(0.0);
|
||||
nrm += wave(vec3(0.165265,0.797878,0.579722), n, cycles[0], 0.0);
|
||||
nrm += wave(vec3(-0.781471,-0.597439,0.179918), n, cycles[1], 0.0);
|
||||
nrm += wave(vec3(-0.198213,-0.434398,-0.878641), n, cycles[2], 0.0);
|
||||
nrm += wave(vec3(-0.386122,-0.899855,-0.202907), n, cycles[3], 0.0);
|
||||
nrm += wave(vec3(0.698788,0.696115,0.164682), n, cycles[4], 0.0);
|
||||
nrm += wave(vec3(-0.949896,0.303522,0.0746418), n, cycles[5], 0.0);
|
||||
return nrm;
|
||||
}
|
||||
|
||||
vec3 waveGroup_1(vec3 n) {
|
||||
vec3 nrm = vec3(0.0);
|
||||
nrm += wave(vec3(-0.646514,-0.557331,-0.520963), n, cycles[0], 1.0);
|
||||
nrm += wave(vec3(-0.901481,0.415232,0.122129), n, cycles[1], 1.0);
|
||||
nrm += wave(vec3(-0.484168,-0.102868,0.868907), n, cycles[2], 1.0);
|
||||
nrm += wave(vec3(0.908183,-0.350865,0.228248), n, cycles[3], 1.0);
|
||||
nrm += wave(vec3(-0.678018,-0.715496,0.168393), n, cycles[4], 1.0);
|
||||
nrm += wave(vec3(-0.680885,0.544192,-0.490153), n, cycles[5], 1.0);
|
||||
return nrm;
|
||||
}
|
||||
|
||||
vec3 waveGroup_2(vec3 n) {
|
||||
vec3 nrm = vec3(0.0);
|
||||
nrm += wave(vec3(-0.460565,-0.0886464,-0.883188), n, cycles[0], 2.0);
|
||||
nrm += wave(vec3(-0.948646,-0.284376,-0.138568), n, cycles[1], 2.1);
|
||||
nrm += wave(vec3(-0.921532,0.114724,-0.370968), n, cycles[2], 2.2);
|
||||
nrm += wave(vec3(-0.514358,-0.623654,0.588635), n, cycles[3], 2.3);
|
||||
nrm += wave(vec3(0.664701,-0.730222,0.157952), n, cycles[4], 2.4);
|
||||
nrm += wave(vec3(-0.648192,-0.645786,-0.403493), n, cycles[5], 2.5);
|
||||
return nrm;
|
||||
}
|
||||
|
||||
vec3 waveGroup_3(vec3 n) {
|
||||
vec3 nrm = vec3(0.0);
|
||||
nrm += wave(vec3(-0.863986,-0.267181,0.42678), n, cycles[0], 3.0);
|
||||
nrm += wave(vec3(-0.391345,0.662204,-0.639011), n, cycles[1], 3.1);
|
||||
nrm += wave(vec3(0.623301,0.247507,-0.741779), n, cycles[2], 3.2);
|
||||
nrm += wave(vec3(0.071236,-0.976658,0.202645), n, cycles[3], 3.3);
|
||||
nrm += wave(vec3(-0.459855,0.703215,0.542238), n, cycles[4], 3.4);
|
||||
nrm += wave(vec3(-0.553626,0.678452,0.482909), n, cycles[5], 3.5);
|
||||
return nrm;
|
||||
}
|
||||
|
||||
vec3 waveGroup_4(vec3 n) {
|
||||
vec3 nrm = vec3(0.0);
|
||||
nrm += wave(vec3(-0.200851,0.941926,0.269135), n, cycles[0], 3.5);
|
||||
nrm += wave(vec3(-0.555743,-0.510164,-0.656416), n, cycles[1], 3.6);
|
||||
nrm += wave(vec3(0.778631,-0.545074,-0.31085), n, cycles[2], 3.7);
|
||||
nrm += wave(vec3(0.497998,-0.636667,-0.588773), n, cycles[3], 3.8);
|
||||
nrm += wave(vec3(-0.914133,0.301103,-0.271472), n, cycles[4], 3.9);
|
||||
nrm += wave(vec3(0.412585,-0.796282,-0.442389), n, cycles[5], 4.0);
|
||||
return nrm;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 color = gl_FrontMaterial.diffuse.rgb;
|
||||
vec4 texSamp = texture2D(diffuseRGBspecA, uv);
|
||||
vec3 matspec = gl_FrontMaterial.specular.rgb * texSamp.a;
|
||||
float shininess = gl_FrontMaterial.shininess;
|
||||
|
||||
vec3 norm = normalize(normal);
|
||||
vec3 v = normalize(npos);
|
||||
|
||||
vec3 nfront = normalize(gl_NormalMatrix * vec3(1.0,0.0,0.0));
|
||||
vec3 nleft = normalize(gl_NormalMatrix * vec3(0.0,0.0,1.0));
|
||||
vec3 nup = cross(nfront,nleft);
|
||||
|
||||
vec3 n = normalize(transpose(gl_NormalMatrix) * normal);
|
||||
vec3 bent =
|
||||
waveGroup_0(n) + waveGroup_1(n) +
|
||||
waveGroup_2(n) + waveGroup_3(n) +
|
||||
waveGroup_4(n);
|
||||
n = normalize(gl_NormalMatrix * bent);
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
|
||||
vec3 diffuse = gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;
|
||||
vec3 dNorm = normalize(n * 0.2 + norm * 0.8);
|
||||
|
||||
vec3 specular = vec3(0.0,0.0,0.0);
|
||||
{ //if(nLightCount > 0)
|
||||
const int i = 0;
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float surfaceNdL = pow(max(dot(norm, light[i]), 0.0), 0.5);
|
||||
float nl = dot(dNorm, light[i]);
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * max(falloff * nl * surfaceNdL, 0.0);
|
||||
|
||||
vec3 r = normalize(-reflect(normalize(light[i]), n));
|
||||
|
||||
float specIntensity = pow(max(0.0, dot(r, v)), shininess);
|
||||
specIntensity += max(pow(max(-dot(light[i],v),0.0),8.0) * pow(1.0 - n.z, 2.0),0.0);
|
||||
|
||||
specular += gl_LightSource[i].specular.rgb * (specIntensity * falloff * surfaceNdL);
|
||||
}
|
||||
{ //if(nLightCount > 1)
|
||||
const int i = 1;
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float surfaceNdL = pow(dot(norm, light[i]), 0.5);
|
||||
float nl = dot(dNorm, light[i]);
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * max(falloff * nl, 0.0);
|
||||
|
||||
vec3 r = normalize(-reflect(normalize(light[i]), n));
|
||||
|
||||
float specIntensity = pow(max(0.0, dot(r, v)), shininess);
|
||||
specIntensity += max(pow(max(-dot(light[i],v),0.0),8.0) * pow(1.0 - n.z, 2.0),0.0);
|
||||
|
||||
specular += gl_LightSource[i].specular.rgb * (specIntensity * falloff);
|
||||
}
|
||||
|
||||
float cityLevel = max(0.0, texture2D(cities, uv).r + (population / (population + 8.0)) - 1.0) * dot(n,norm);
|
||||
|
||||
gl_FragColor.rgb = (diffuse * color) * texSamp.rgb + (specular * matspec) + cityLevel * vec3(1.0,0.9,0.55);
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#define pi 3.141592653589793238462643383279
|
||||
#define twopi (pi * 2.0)
|
||||
varying vec2 uv;
|
||||
varying vec2 rcoords;
|
||||
|
||||
uniform float circle_min;
|
||||
uniform float circle_max;
|
||||
|
||||
uniform vec4 captureColor;
|
||||
uniform float capturePct;
|
||||
|
||||
vec4 alphaBlend(vec4 dest, vec4 src) {
|
||||
float alpha = src.a + (dest.a * (1.0 - src.a));
|
||||
return vec4( ((src.rgb * src.a) + (dest.rgb * dest.a * (1.0 - src.a))) / alpha, alpha);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float radius = length(rcoords);
|
||||
if(radius > circle_max || radius < circle_min)
|
||||
discard;
|
||||
|
||||
vec4 result = gl_Color;
|
||||
result.a *= smoothstep(circle_min, circle_max, radius);
|
||||
|
||||
//Dither the gradient
|
||||
float rnd = dot(rcoords, vec2(0.2,0.3));
|
||||
result.a += mod(rnd/max(mod(uv.x,0.01)/0.01, 0.001), 0.01)-0.005;
|
||||
|
||||
if(captureColor.a > 0.0) {
|
||||
vec2 rcoords = (uv - vec2(0.5, 0.5)) * 2.0;
|
||||
if(radius > 0.9 && radius < 1.0) {
|
||||
float ang = (atan(rcoords.y, rcoords.x) + pi) / twopi;
|
||||
if(ang < capturePct) {
|
||||
float alpha = smoothstep(1.0, 0.95, radius) * smoothstep(0.9, 0.95, radius);
|
||||
alpha *= smoothstep(0.0, 0.01, ang) * smoothstep(capturePct, capturePct - 0.01, ang);
|
||||
|
||||
result = alphaBlend(result, vec4(captureColor.rgb, captureColor.a * alpha));
|
||||
//result = mix( result, vec4(captureColor.rgb, 1.0), alpha * captureColor.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
gl_FragColor = result;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
varying vec2 uv;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main() {
|
||||
vec3 sample = texture2D(texture, vec2(uv.x, (uv.y - 0.45) / 0.2)).rgb;
|
||||
|
||||
if(uv.y < 0.45 || uv.y > 0.65)
|
||||
discard;
|
||||
float a = smoothstep(0.45, 0.46, uv.y);
|
||||
a = min(a, 1.0 - smoothstep(0.64, 0.65, uv.y));
|
||||
a = min(a, smoothstep(0.00, 0.05, uv.x));
|
||||
a = min(a, 1.0 - smoothstep(0.95, 1.00, uv.x));
|
||||
|
||||
gl_FragColor.rgb = mix(gl_Color.rgb * sample, vec3(0.0, 0.0, 0.0), 1.0 - a);
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
varying vec3 npos, origo;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec2 vertLightMask;
|
||||
uniform int flagNumber;
|
||||
uniform vec3 wsPos;
|
||||
uniform vec4 wsRot;
|
||||
|
||||
varying vec3 light[nLightCount];
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
float square(float x){
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 wsAllign(vec3 x){
|
||||
return x + 2.0 * cross(wsRot.xyz, cross(wsRot.xyz, x) + wsRot.w * x);
|
||||
}
|
||||
void main()
|
||||
{
|
||||
pos = gl_ModelViewMatrix * in_vertex;
|
||||
|
||||
// convert view, normal and light vectors to world space and quaternion correct for model rotation
|
||||
mat3 tcamrot = transpose(mat3x3(gl_ModelViewMatrix));
|
||||
npos = (wsAllign(normalize(tcamrot * -pos.xyz)));
|
||||
|
||||
normal = (tcamrot * (gl_NormalMatrix * wsAllign(normalize(in_normal))));
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = wsAllign(normalize((tcamrot * (((gl_LightSource[i].position)).xyz + pos.xyz))));
|
||||
}
|
||||
|
||||
// store origo for model alligned effects
|
||||
origo = in_vertex.xyz;
|
||||
|
||||
// prep vertex masks
|
||||
vertMasksSecondary = max(vec3(0.0), in_color.rgb * 3.0 -2.0); // shield r, windows g, engine b
|
||||
vertMasksPrimary = max(vec3(0.0), (1.0 - in_color.rgb) * 3.0 - 2.0); // flags g, warp b, transparency on r
|
||||
vertLightMask = vec2(1.0 - vertMasksSecondary.r - vertMasksSecondary.g - vertMasksSecondary.b - vertMasksPrimary.b, floor(1.0 - vertMasksSecondary.b));
|
||||
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
|
||||
// prep flag uv
|
||||
uv2 = uv2.xy + vec2((1.0 / 32.0) * clamp(float(flagNumber), 0.0, 20.0) * vertMasksPrimary.g, 0.0);
|
||||
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.y = 1.0 - uv3.y;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
varying vec3 npos, origo;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying float vertLightMask;
|
||||
uniform int flagNumber;
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
normal = normalize(gl_NormalMatrix * in_normal);
|
||||
|
||||
vec4 pos = gl_ModelViewMatrix * in_vertex;
|
||||
npos = -pos.xyz;
|
||||
origo = in_vertex.xyz;
|
||||
|
||||
vertMasksSecondary = max(vec3(0.0), in_color.rgb * 3.0 -2.0); // shield r, windows g, engine b
|
||||
vertMasksPrimary = max(vec3(0.0), (1.0 - in_color.rgb) * 3.0 - 2.0); // flags g, warp b, transparency on r
|
||||
vertLightMask = 1.0 - vertMasksSecondary.r - vertMasksSecondary.g - vertMasksSecondary.b - vertMasksPrimary.b; // filter off no-special lights
|
||||
|
||||
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
|
||||
uv2 = uv2.xy + vec2((1.0 / 32.0) * clamp(flagNumber, 0, 20) * vertMasksPrimary.g, 0.0);
|
||||
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.y = 1.0-uv3.y;
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
varying vec3 npos, origo;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec2 vertLightMask;
|
||||
uniform vec4 wsRot;
|
||||
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 lightColor[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
float square(float x){
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 wsAllign(vec3 x){
|
||||
return x + 2.0 * cross(wsRot.xyz, cross(wsRot.xyz, x) + wsRot.w * x);
|
||||
}
|
||||
void main()
|
||||
{
|
||||
pos = gl_ModelViewMatrix * in_vertex;
|
||||
|
||||
// convert view, normal and light vectors to world space and quaternion correct for model rotation
|
||||
mat3 tcamrot = transpose(mat3x3(gl_ModelViewMatrix));
|
||||
npos = (wsAllign(normalize(tcamrot * -pos.xyz)));
|
||||
|
||||
normal = (tcamrot * (gl_NormalMatrix * wsAllign(normalize(in_normal))));
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = wsAllign(normalize((tcamrot * (((gl_LightSource[i].position)).xyz - pos.xyz))));
|
||||
}
|
||||
|
||||
// store origo for model alligned effects
|
||||
origo = in_vertex.xyz;
|
||||
|
||||
// prep vertex masks
|
||||
vertMasksSecondary = max(vec3(0.0), in_color.rgb * 3.0 -2.0); // shield r, windows g, engine b
|
||||
vertMasksPrimary = max(vec3(0.0), (1.0 - in_color.rgb) * 3.0 - 2.0); // flags g, warp b, transparency on r
|
||||
vertLightMask = vec2(1.0 - vertMasksSecondary.r - vertMasksSecondary.g - vertMasksSecondary.b - vertMasksPrimary.b, floor(1.0 - vertMasksSecondary.b));
|
||||
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.y = 1.0 - uv3.y;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
varying vec3 npos, origo;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying float vertLightMask;
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
normal = normalize(gl_NormalMatrix * in_normal);
|
||||
|
||||
vec4 pos = gl_ModelViewMatrix * in_vertex;
|
||||
npos = -pos.xyz;
|
||||
origo = in_vertex.xyz;
|
||||
|
||||
vertMasksSecondary = max(vec3(0.0), in_color.rgb * 3.0 -2.0); // shield r, windows g, engine b
|
||||
vertMasksPrimary = max(vec3(0.0), (1.0 - in_color.rgb) * 3.0 - 2.0); // flags g, warp b, transparency on r
|
||||
vertLightMask = 1.0 - vertMasksSecondary.r - vertMasksSecondary.g - vertMasksSecondary.b - vertMasksPrimary.b; // filter off no-special lights
|
||||
|
||||
// ogex uv's are
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.y = 1.0 - uv3.y;
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
varying vec3 npos, origo;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec3 vertMasks;
|
||||
uniform vec4 wsRot;
|
||||
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 lightColor[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
float square(float x){
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 wsAllign(vec3 x){
|
||||
return x + 2.0 * cross(wsRot.xyz, cross(wsRot.xyz, x) + wsRot.w * x);
|
||||
}
|
||||
void main()
|
||||
{
|
||||
pos = gl_ModelViewMatrix * in_vertex;
|
||||
|
||||
// convert view, normal and light vectors to world space and quaternion correct for model rotation
|
||||
mat3 tcamrot = transpose(mat3x3(gl_ModelViewMatrix));
|
||||
npos = (wsAllign(normalize(tcamrot * -pos.xyz)));
|
||||
|
||||
normal = (tcamrot * (gl_NormalMatrix * wsAllign(normalize(in_normal))));
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = wsAllign(normalize((tcamrot * (((gl_LightSource[i].position)).xyz - pos.xyz))));
|
||||
}
|
||||
|
||||
// store origo for model alligned effects
|
||||
origo = in_vertex.xyz;
|
||||
|
||||
vertMasks = max(vec3(0.0), in_color.rgb * 3.0 -2.0); // shield r, windows g, engine b
|
||||
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.y = 1.0-uv3.y;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
varying vec3 npos, origo;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec3 vertMasks;
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
normal = normalize(gl_NormalMatrix * in_normal);
|
||||
|
||||
vec4 pos = gl_ModelViewMatrix * in_vertex;
|
||||
npos = -pos.xyz;
|
||||
origo = in_vertex.xyz;
|
||||
|
||||
vertMasks = max(vec3(0.0), in_color.rgb * 3.0 -2.0); // shield r, windows g, engine b
|
||||
|
||||
// ogex uv's are
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.y = 1.0 - uv3.y;
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
varying vec3 npos, origo;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying float vertLightMask;
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
normal = normalize(gl_NormalMatrix * in_normal);
|
||||
|
||||
vec4 pos = gl_ModelViewMatrix * in_vertex;
|
||||
npos = -pos.xyz;
|
||||
origo = in_vertex.xyz;
|
||||
|
||||
vertMasksSecondary = max(vec3(0.0), in_color.rgb * 3.0 -2.0); // shield r, windows g, engine b
|
||||
vertMasksPrimary = max(vec3(0.0), (1.0 - in_color.rgb) * 3.0 - 2.0); // flags g, warp b, transparency on r
|
||||
vertLightMask = 1.0 - vertMasksSecondary.r - vertMasksSecondary.g - vertMasksSecondary.b - vertMasksPrimary.b; // filter off no-special lights
|
||||
|
||||
// ogex uv's are
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.y = 1.0 - uv3.y;
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
const float pi = 3.14159265358;
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
uniform sampler2D wreckage;
|
||||
uniform samplerCube skybox;
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform float life;
|
||||
varying vec4 pos;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.001, 0.0005);
|
||||
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
|
||||
// get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// Schlick GGX approximation
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow(1.0 - clamp((dot(l, h)), 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec2 duv1Calc(vec2 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec2 duv2Calc(vec2 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uvP = uv;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec2 duv1 = duv1Calc(uvP);
|
||||
vec2 duv2 = duv2Calc(uvP);
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
float p = texture2D(wreckage, uvP).a * scaleBias.r - scaleBias.g;
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
float sampDiscard = texture2D(wreckage, uvP, 0.0).a;
|
||||
if (sampDiscard <= 0.025f)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
}
|
||||
else{
|
||||
float sampDiscard = texture2D(wreckage, uvP, 0.0).a;
|
||||
if (sampDiscard <= 0.025f)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
float sampDiscard = texture2D(wreckage, uvP, 0.0).a;
|
||||
if (sampDiscard <= 0.025f)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vec3 sampData = texture2D(wreckage, uvP).rgb;
|
||||
|
||||
float metalness = min(1.0, sampData.b * 4.0);
|
||||
sampData.xy *= 2.0;
|
||||
sampData.xy -= 1.0;
|
||||
|
||||
// Roughness setup
|
||||
float orgRoughness = sampData.b * 0.5 + 0.25;
|
||||
// Albedo setup
|
||||
vec3 albedo = toLinear(mix(vec3(0.271, 0.247, 0.216), vec3(0.541, 0.518, 0.502), sampData.b));
|
||||
|
||||
orgRoughness += 0.5;
|
||||
n = normalize(TBN * normalize(vec3(sampData.xy, deriveZ(sampData.xy))));
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
// Substance setup and Albedo adjust
|
||||
vec3 substance = clamp((0.04 - 0.04 * metalness) + albedo * metalness,0.0, 1.0);
|
||||
albedo -= substance;
|
||||
|
||||
// Light model specials setup
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (sampData.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
color += (albedo * (sampData.b * 0.5) + pow(S * HdotN, vec3(sampData.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
|
||||
float fadeCurve = clamp(1.0 - pow(life, 0.33) + 0.5, 0.0, 1.0);
|
||||
|
||||
gl_FragColor.rgb = toGamma(color + toLinear(mix(vec3(0.69, 0.231, 0.027), vec3(1.0, 0.969, 0.6), fadeCurve) * fadeCurve * 5.0 * sampData.b));
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
const float pi = 3.14159265358;
|
||||
|
||||
uniform sampler2D diffuse, masks, normals;
|
||||
uniform samplerCube skybox;
|
||||
//uniform vec4 ownerColor;
|
||||
//uniform vec3 glowColor;
|
||||
//uniform float thrust;
|
||||
//uniform float camDist;
|
||||
uniform float life;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
uniform mat3 invView;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// Schlick GGX approximation
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow(1.0 - clamp((dot(l, h)), 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
|
||||
float RoughToSPow(float fRoughness)
|
||||
{
|
||||
return (2.0 / (fRoughness * fRoughness)) - 2.0;
|
||||
}
|
||||
|
||||
const float k0 = 0.00098f, k1 = 0.9921f;
|
||||
const float g_fMaxT = (exp2(-10.0 / sqrt((2.0 / (0.0014f * 0.0014f)) - 2.0)) - 0.00098f) / 0.9921f;
|
||||
|
||||
float GetSpecPowToMip(float fSpecPow, int nMips)
|
||||
{
|
||||
float fSmulMaxT = (exp2( -10.0 / sqrt(fSpecPow)) - k0) / k1;
|
||||
|
||||
return float(nMips - 1) * (1.0 - clamp(fSmulMaxT / g_fMaxT, 0.0, 1.0 ));
|
||||
}
|
||||
|
||||
// screen-space cotangent derivative
|
||||
mat3 cotangent_frame(vec3 N, vec3 p, vec2 uv)
|
||||
{
|
||||
vec4 dpduv1 = dFdx(vec4(p.xy, uv));
|
||||
vec4 dpduv2 = dFdy(vec4(p.xy, uv));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(vec3(dpduv2.xy, 0.0), N);
|
||||
vec3 dp1perp = cross(N, vec3(dpduv1.xy, 0.0));
|
||||
vec3 T = dp2perp * dpduv1.z + dp1perp * dpduv2.z;
|
||||
vec3 B = dp2perp * dpduv1.w + dp1perp * dpduv2.w;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(T, T), dot(B, B)), -0.5);
|
||||
return mat3(T * invmax, B * invmax, N);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 diffuse = texture2D(diffuse, uv.xy, -10.0);// discard will screw up if mipmapping
|
||||
if (diffuse.a < 1.0f)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
vec4 normSamp = texture2D(normals, uv.xy);
|
||||
vec4 maskSamp = texture2D(masks, uv.xy);
|
||||
|
||||
float transparency = clamp((clamp(diffuse.w,0.25, 0.5) - 0.25), 0.0, 0.25) * 4 + 0.25;
|
||||
float metalness = maskSamp.b;
|
||||
float ao = normSamp.a;
|
||||
vec3 normMap = normSamp.xyz * 2.0 - 1.0;
|
||||
|
||||
// Roughness setup
|
||||
float orgRoughness = maskSamp.r;
|
||||
|
||||
// Albedo setup
|
||||
vec3 albedo = diffuse.rgb;
|
||||
|
||||
mat3 TBN = cotangent_frame(normal, -npos, uv.xy);
|
||||
vec3 n = normalize(TBN * normMap);
|
||||
vec3 v = normalize(npos);
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// Substance setup and Albedo adjust
|
||||
vec3 substance = clamp((0.04 - 0.04 * metalness) + albedo * metalness,0.0, 1.0);
|
||||
albedo -= substance;
|
||||
|
||||
// Light model specials setup
|
||||
float NdotV = clamp(dot(n, v), 0.0, 1.0);
|
||||
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
// Ambient reflections
|
||||
vec3 reflectionFresnel = Fresnel2(substance,NdotV,orgRoughness);
|
||||
|
||||
color += textureCube(skybox, r,8.0 - (8.0*(1.0-orgRoughness))).rgb * reflectionFresnel;
|
||||
|
||||
// Ambient light
|
||||
color += textureCube(skybox, n, 7.0).rgb * albedo * (1.0 - reflectionFresnel);
|
||||
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
|
||||
// fakes disc like light up close to a star.
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
|
||||
|
||||
float NdotL = clamp(dot(n,light[i]),0.0, 1.0);
|
||||
|
||||
float intensity = falloff * NdotL;// * clamp(dot(normal, light[i]), 0.0, 1.0);
|
||||
|
||||
vec3 L=light[i];
|
||||
L = normalize(L);
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
|
||||
float HdotN = clamp(dot(halfVec,n), 0.0, 1.0);
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = clamp(D_GGX(HdotN, orgRoughness), 0.0, 1.0);
|
||||
float V = clamp(V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL), 0.0, 1.0);
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += (D * V * F + O * albedo + max(vec3(0.0),1.0 - (1.0 + F))) * pi * gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
}
|
||||
|
||||
float fadeCurve = clamp(1.0 - pow(life, 0.33) + 0.5, 0.0, 1.0) * (1.0 - maskSamp.g);
|
||||
|
||||
gl_FragColor.rgb = color + mix(vec3(0.69, 0.231, 0.027), vec3(1.0, 0.969, 0.6), fadeCurve) * fadeCurve * 6.0;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
|
||||
uniform vec4 wsRot;
|
||||
|
||||
varying vec3 npos;
|
||||
varying vec3 normal;
|
||||
varying vec2 uv;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec4 pos;
|
||||
|
||||
float square(float x){
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 wsAllign(vec3 x){
|
||||
return x + 2.0 * cross(wsRot.xyz, cross(wsRot.xyz, x) + wsRot.w * x);
|
||||
}
|
||||
void main()
|
||||
{
|
||||
pos = gl_ModelViewMatrix * in_vertex;
|
||||
|
||||
// convert view, normal and light vectors to world space and quaternion correct for model rotation
|
||||
mat3 tcamrot = transpose(mat3x3(gl_ModelViewMatrix));
|
||||
npos = (wsAllign(normalize(tcamrot * -pos.xyz)));
|
||||
|
||||
normal = (tcamrot * (gl_NormalMatrix * wsAllign(normalize(in_normal))));
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = wsAllign(normalize((tcamrot * (((gl_LightSource[i].position)).xyz - pos.xyz))));
|
||||
}
|
||||
|
||||
uv = in_uv;
|
||||
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,521 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// shield color hardcoded to match the ship shield effect
|
||||
const vec3 shieldColor = vec3(0.3, 0.9,1.0);
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform float acceleration;
|
||||
uniform float velocity;
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec2 vertLightMask;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// shield effect
|
||||
float shieldEffect(vec2 uv, vec3 t)
|
||||
{
|
||||
vec2 uvRad = uv - 0.5;
|
||||
float falloff = dot(uvRad, uvRad) * 3.0;
|
||||
if (simpleProcedurals){
|
||||
//source intensity levels
|
||||
vec2 pulse = 2.0 * uv + 2.0;
|
||||
pulse.x *= 2.0;
|
||||
pulse *= 4.0;
|
||||
|
||||
//frequency of radial ring ripples
|
||||
float radialRipples = sin(pulse.x + sin(t.x + pulse.x * falloff)) * sin(pulse.y + sin(t.x * 0.5));
|
||||
|
||||
float shield = 0.0;
|
||||
shield = radialRipples;
|
||||
pulse *= 0.5;
|
||||
pulse = abs(pulse);
|
||||
pulse *= falloff;
|
||||
pulse.x += sin(pulse.y * sin(pulse.x));
|
||||
|
||||
//frequency of ring ripples
|
||||
float ripples = sin(pulse.x + t.x);
|
||||
|
||||
shield += ripples;
|
||||
shield *= ripples * radialRipples;
|
||||
shield = max(0.0,min(1.0,shield)) * 0.33;
|
||||
shield += falloff * 0.33;
|
||||
|
||||
//end intensity levels
|
||||
shield *= 4.0 * falloff;
|
||||
return shield;
|
||||
}
|
||||
else{
|
||||
return falloff;
|
||||
}
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = (mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * scaleBias.r - scaleBias.g) * (1.0 - vertMasksSecondary.b);
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
// create shields - detail level is inclueded in the subfunction
|
||||
float shields = shieldEffect(uv, t) * vertMasksSecondary.r;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow(nodeScale, 0.5)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasksSecondary.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasksPrimary.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// player color setup
|
||||
vec4 playerPlateProfile = vec4(vec3(min(vec3(1.0), (1.0 - ownerColor.rgb) * 0.15 + 0.85)), 0.45);
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.85 + 0.15;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.85 + 0.15;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
// Engine setup
|
||||
float thrust = clamp((velocity * acceleration * 0.025), 0.0, 1.0);
|
||||
vec3 engineColor = (blackBody(1000.0 + 6000.0 * square(thrust)));
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * (1.0 - vertMasksPrimary.r) * colorLightsWindows;
|
||||
lights += uniqueEmissives.b * engineColor * (thrust + 0.1) * vertMasksPrimary.r;
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * vertLightMask.r * colorLightsPrimary;
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
detailLights += (vertMasksSecondary.g * lightMask.x) * colorLightsWindows * vertLightMask.g;
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
if (simpleProcedurals){
|
||||
// makes shields flicker if area is damaged
|
||||
shields = mix(shields, abs(shields * sin(t.y) * sin(t.z)), damageMask.r);
|
||||
}
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao
|
||||
color *= transAoPlatesMetal.g;
|
||||
|
||||
// shading is revomed from the shields and lights are added
|
||||
color *= (1.0 - vertMasksSecondary.r);
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// shields are added
|
||||
color += mix(shieldColor, vec3(1.0), shields * 0.5) * vertMasksSecondary.r;
|
||||
// engines are added
|
||||
if (advancedProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 4)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasksSecondary.b;
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 2)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasksSecondary.b;
|
||||
}
|
||||
else{
|
||||
color += engineColor * vertMasksSecondary.b;
|
||||
}
|
||||
// shields transparency is set
|
||||
transAoPlatesMetal.r = 1.0 - vertMasksSecondary.r;
|
||||
transAoPlatesMetal.r += clamp(shields, 0.0, 1.0);
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = transAoPlatesMetal.r;
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
// pirate
|
||||
const vec3 colorLightsPrimary = vec3(0.949, 0.424, 0.31);
|
||||
const vec3 colorLightsSecondary = vec3(0.929, 0.091, 0.041);
|
||||
// certain plate parts are made brighter and more shiny.
|
||||
const vec4 playerPlateProfile = vec4(0.48,0.45,0.42,0.15);
|
||||
const vec4 ownerColor = vec4(0.55, 0.25, 0.25, 1.0);
|
||||
|
||||
//// remnant
|
||||
//const vec3 colorLightsPrimary = vec3(0.31, 0.749, 0.624);
|
||||
//const vec3 colorLightsSecondary = vec3(0.041, 0.749, 0.929);
|
||||
//
|
||||
//// certain plate parts are made brighter and more shiny.
|
||||
//const vec4 playerPlateProfile = vec4(0.48, 0.45, 0.42, 0.45);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform float acceleration;
|
||||
uniform float velocity;
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 vertMasks;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = (mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * scaleBias.r - scaleBias.g) * (1.0 - vertMasks.b);
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow(nodeScale, 0.5)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasks.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasks.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + maskSamp.b * 0.25 + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
// Engine setup
|
||||
float thrust = clamp((velocity * acceleration * 0.025), 0.0, 1.0);
|
||||
vec3 engineColor = (blackBody(1000.0 + 6000.0 * square(thrust)));
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * engineColor * (thrust + 0.1);
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * colorLightsPrimary * (1.0 - floor(vertMasks.b + 0.1));
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao
|
||||
color *= transAoPlatesMetal.g;
|
||||
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// engines are added
|
||||
if (advancedProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 4)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasks.b;
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 2)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasks.b;
|
||||
}
|
||||
else{
|
||||
color += engineColor * vertMasks.b;
|
||||
}
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
const float pi = 3.14159265358;
|
||||
|
||||
uniform sampler2D diffuse, normalAOlights, shield, masks;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform vec3 glowColor;
|
||||
uniform float thrust;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
|
||||
float teamNumber = 5;
|
||||
|
||||
//varying vec4 shadowCoord[nLightCount];
|
||||
varying vec3 forward, right;
|
||||
varying vec3 normal, binormal, tangent;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 vertPaint;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
/*
|
||||
//Skybox sampling call
|
||||
vec2 skyboxSample(vec3 dir) {
|
||||
float x = dot(dir, forward);
|
||||
float z = dot(dir, right);
|
||||
float y = dot(dir, cross(forward,right));
|
||||
|
||||
return vec2(atan(-x,z) / 6.28318530718, (y + 1.0) * 0.5);
|
||||
}
|
||||
*/
|
||||
//Engine heat color call
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
if(temp < 6600.0)
|
||||
c.g = mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0));
|
||||
else
|
||||
c.g = mix(0.976, 0.75, mixRange(temp, 6600.0, 29800.0));
|
||||
return c;
|
||||
}
|
||||
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// Schlick GGX approximation
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow(1.0 - clamp((dot(l, h)), 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
|
||||
float RoughToSPow(float fRoughness)
|
||||
{
|
||||
return (2.0 / (fRoughness * fRoughness)) - 2.0;
|
||||
}
|
||||
|
||||
const float k0 = 0.00098f, k1 = 0.9921f;
|
||||
const float g_fMaxT = (exp2(-10.0 / sqrt((2.0 / (0.0014f * 0.0014f)) - 2.0)) - 0.00098f) / 0.9921f;
|
||||
|
||||
float GetSpecPowToMip(float fSpecPow, int nMips)
|
||||
{
|
||||
float fSmulMaxT = (exp2( -10.0 / sqrt(fSpecPow)) - k0) / k1;
|
||||
|
||||
return float(nMips - 1) * (1.0 - clamp(fSmulMaxT / g_fMaxT, 0.0, 1.0 ));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 diffuse = texture2D(diffuse, uv.xy);
|
||||
vec4 normAO = texture2D(normalAOlights, uv.xy);
|
||||
vec3 maskSamp = texture2D(masks, uv.xy).rgb;
|
||||
float paintSamp = texture2D(masks, uv2.xy).w;
|
||||
float flagSamp = texture2D(masks, uv2.xy + vec2((1.0/32.0) * teamNumber, 0.0)).w;
|
||||
vec3 vertMasksPrimary = vertPaint.rgb; // .a unused
|
||||
//float texSamp7 = texture2D(shield, uv2.xy * 10);
|
||||
//float texSamp8 = texture2D(shield, uv2.xy - time * 80.0);
|
||||
float texSamp5 = texture2D(shield, uv2.xy - 8.0 * 24.0).r;
|
||||
float texSamp6 = texture2D(shield, uv2.xy + time * 24.0).g;
|
||||
|
||||
// Normal map setup
|
||||
vec3 normMap = normAO.xyz * 2.0 - vec3(1.0);
|
||||
|
||||
// Vertex masks setup
|
||||
vec3 vertMasksSecondary = (clamp(vertMasksPrimary, 0.5, 1.0) - 0.5) * 2.0; // shield r, windows g, engine b
|
||||
vertMasksPrimary = clamp(((1.0 - vertMasksPrimary) - 0.5)* 2.0, 0.0, 1.0); // flags g, warp b, transparency on r
|
||||
|
||||
// Texture masks setup
|
||||
float paintMask = (flagSamp * vertMasksPrimary.g + paintSamp * (1.0-vertMasksPrimary.g)) * maskSamp.b;
|
||||
float plateMask = maskSamp.b * (1.0-paintMask);
|
||||
float roughnessTweakMask = plateMask * (1.0-paintMask);
|
||||
|
||||
float lightsOne = (clamp(1.0 - normAO.w, 0.5, 1.0) - 0.5) * 2.0;
|
||||
float lightsTwo = (clamp(normAO.w, 0.5, 1.0) - 0.5) * 2.0;
|
||||
|
||||
// Emissive lights setup
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
float lightMask = 1.0 - vertMasksSecondary.r - vertMasksSecondary.g - vertMasksSecondary.b - vertMasksPrimary.b; // filter for no-special lights
|
||||
|
||||
// Primary and secondary lights are added - should be selectable, as owner and inverted owner is not always nice
|
||||
lights += vec3(lightsOne * lightMask * ownerColor.rgb * 2.0);
|
||||
lights += vec3(lightsTwo * lightMask * (1.0 - ownerColor.rgb) * 2.0);
|
||||
|
||||
// Warp drive is added - currently no models contains the mask.
|
||||
float engineGlow = texSamp5 * texSamp6;
|
||||
// lights += vec3(2.0, 0.0, 2.0) * (1.0 + engineGlow) * lightsTwo * vertMasksPrimary.b;
|
||||
|
||||
// Windows are added
|
||||
lights += glowColor * vertMasksSecondary.g * lightsOne;
|
||||
|
||||
// Engine animation is created
|
||||
engineGlow = lightsOne * (/*lightsOne * 2.0 + */engineGlow * 2.0) * vertMasksSecondary.b;
|
||||
|
||||
// Gives nice intensity and falloff transitions.
|
||||
lights = pow(lights + (lightsOne + lightsTwo) * 1.0, vec3(1.5)) * (1.0 - vertMasksPrimary.r);
|
||||
|
||||
// Transparency setup
|
||||
float transparency = min(clamp((clamp(diffuse.w,0.25, 0.5) - 0.25), 0.0, 0.25) * 4 + 0.25, 1.0-vertMasksPrimary.r);
|
||||
|
||||
// Transparency and ao extractionsetup
|
||||
float ao = (1.0-((1.0-transparency) * 0.5 + diffuse.w))*2.0;
|
||||
|
||||
// Engine setup
|
||||
float thrustIntensity = pow(thrust * 1.0, 2.0) * engineGlow;
|
||||
lights += (blackBody(1000.0 + 5000.0 * thrustIntensity) * thrustIntensity);
|
||||
|
||||
|
||||
// Player color setup - move the PBR values to external source!
|
||||
// float playerMetal = 0.01; //PBR gold
|
||||
// vec3 playerMetalCol = vec3(1.0, 0.77, 0.33); //PBR gold
|
||||
// float playerMetal = 0.25; //PBR bronze
|
||||
// vec3 playerMetalCol = vec3(0.99, 0.64, 0.34); //PBR bronze
|
||||
// float playerMetal = 0.13; //PBR brass
|
||||
// vec3 playerMetalCol = vec3(0.98, 0.87, 0.41); //PBR brass
|
||||
// float playerMetal = 0.35; //PBR Copper
|
||||
// vec3 playerMetalCol = vec3(0.98, 0.60, 0.52); //PBR Copper
|
||||
// float playerMetal = 0.10; //PBR Silver
|
||||
// vec3 playerMetalCol = vec3(0.97, 0.96, 0.91); //PBR Silver
|
||||
// float playerMetal = 0.01; //PBR Chrome
|
||||
// vec3 playerMetalCol = vec3(0.91, 0.95, 0.97); //PBR Chrome
|
||||
// float playerMetal = 0.45; //PBR Titanium
|
||||
// vec3 playerMetalCol = vec3(0.54, 0.49, 0.46); //PBR Titanium
|
||||
// float playerMetal = 0.15; //PBR Bright Steel
|
||||
// vec3 playerMetalCol = vec3(0.84, 0.85, 0.90); //PBR Bright Steel
|
||||
// float playerMetal = 0.75; //PBR Dull Steel
|
||||
// vec3 playerMetalCol = vec3(0.56, 0.57, 0.58); //PBR Dull Steel
|
||||
// float playerMetal = 0.25; //PBR Dark Steel
|
||||
// vec3 playerMetalCol = vec3(0.48, 0.45, 0.42) ; //PBR Dark Steel
|
||||
// float playerMetal = 0.27; //PBR Gallium
|
||||
// vec3 playerMetalCol = vec3(0.88, 0.93, 0.56); //PBR Gallium
|
||||
// float playerMetal = 0.35; //PBR Molybdenum
|
||||
// vec3 playerMetalCol = vec3(0.16, 0.22, 0.15); //PBR Molybdenum
|
||||
// float playerMetal = 0.33; //PBR Niobium
|
||||
// vec3 playerMetalCol = vec3(0.38, 0.34, 0.62); //PBR Niobium
|
||||
// float playerMetal = 0.35; //PBR Vanadium
|
||||
// vec3 playerMetalCol = vec3(0.08, 0.10, 0.09); //PBR Vanadium
|
||||
// float playerMetal = 0.25; //PBR Adamantium
|
||||
// vec3 playerMetalCol = vec3(0.26, 0.18, 0.25); //PBR Adamantium
|
||||
// float playerMetal = 0.07; //PBR Tritinium
|
||||
// vec3 playerMetalCol = vec3(0.54, 0.49, 0.62); //PBR Tritinium
|
||||
// float playerMetal = 0.03; //PBR Zentronium
|
||||
// vec3 playerMetalCol = vec3(0.96, 0.48, 0.43); //PBR Zentronium
|
||||
// float playerMetal = 0.16; //PBR Xintinium
|
||||
// vec3 playerMetalCol = vec3(0.48, 0.96, 0.96); //PBR Xintinium
|
||||
// float playerMetal = 0.15; //PBR Duranium
|
||||
// vec3 playerMetalCol = vec3(0.94, 0.24, 0.02); //PBR Duranium
|
||||
// float playerMetal = 1.0; //PBR Carbon Fibre Complimentary
|
||||
// vec3 playerMetalCol = vec3(1.0 - ownerColor) * 0.25; //PBR Carbon Fibre Complimentary
|
||||
// float playerMetal = 0.9; //PBR Ceramic Complimentary
|
||||
// vec3 playerMetalCol = vec3(1.0 - ownerColor) * 0.64 + vec3(0.32); //PBR Ceramic Complimentary
|
||||
// float playerMetal = 1.0; //PBR Paint Complimentary
|
||||
// vec3 playerMetalCol = vec3(1.0 - ownerColor); //PBR Paint Complimentary
|
||||
float playerMetal = 0.15; //PBR Metal Complimentary
|
||||
vec3 playerMetalCol = vec3(1.0 - ownerColor) * 0.5 + vec3(0.5); //PBR Metal Complimentary
|
||||
|
||||
vec4 playerPlateProfile = vec4(playerMetalCol, playerMetal);
|
||||
|
||||
// PBR mixer
|
||||
playerMetal = clamp(playerMetal, 0.001, 1.0) * plateMask;
|
||||
vec3 paintColor = ownerColor.rgb * (1.0-paintMask);
|
||||
float metalness = clamp(maskSamp.g * max(0.5, 1.0-paintMask), 0.0, 1.0);
|
||||
|
||||
// Roughness setup
|
||||
float orgRoughness = maskSamp.r * (1.0-plateMask) + plateMask * (plateMask * playerPlateProfile.w);
|
||||
|
||||
|
||||
// Albedo setup
|
||||
vec3 albedo = mix(diffuse.rgb * plateMask * playerMetalCol, ownerColor.rgb * diffuse.rgb, paintMask);
|
||||
albedo = mix(diffuse.rgb, albedo, maskSamp.b);
|
||||
|
||||
// Shield setup.
|
||||
lights *= (1.0 - vertMasksSecondary.r);
|
||||
//vertMasksSecondary.r * (1.0 - abs(texSamp5 * texSamp6 * 2.0 - 1.0)) * vec3(0.33, 0.99, 2.0);
|
||||
// transparency *= vertMasksSecondary.r * (1.0 - abs(texSamp5 * texSamp6 * 2.0 - 1.0)) * 0.66;
|
||||
transparency *= clamp(1.0 - vertMasksSecondary.r * 0.5, 0.0, 1.0);
|
||||
transparency += (texSamp5 * texSamp6);// * vertMasksSecondary.r;
|
||||
lights += transparency * vec3(0.33, 0.99, 2.0) * vertMasksSecondary.r;
|
||||
//Adjust all other PBR textures accordingly
|
||||
ao *= 1.0 - vertMasksSecondary.r;
|
||||
orgRoughness *= 1.0 - vertMasksSecondary.r;
|
||||
metalness *= 1.0 - vertMasksSecondary.r;
|
||||
albedo *= 1.0-vertMasksSecondary.r;
|
||||
normMap = normalize(mix(normMap, vec3(0.0,0.0,1.0), vertMasksSecondary.r));
|
||||
|
||||
// Normal setup
|
||||
vec3 n = normalize(normal) * normMap.z;
|
||||
n += normalize(binormal) * normMap.y;
|
||||
n += normalize(tangent) * normMap.x;
|
||||
n = normalize(n);
|
||||
vec3 v = normalize(npos);
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// Substance setup and Albedo adjust
|
||||
vec3 substance = clamp((0.04 - 0.04 * metalness) + albedo * metalness,0.0, 1.0);
|
||||
albedo -= substance;
|
||||
|
||||
// Light model specials setup
|
||||
float NdotV = clamp(dot(n, v), 0.0, 1.0);
|
||||
float roughnessV = (orgRoughness + 1.0)/2.0; // roughness remapping
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = 0.0;
|
||||
if(camDist > 1.0)
|
||||
highlight += smoothstep(0.0, 500.0, camDist / sqrt(nodeScale)) * 0.2;
|
||||
vec3 color = vec3(highlight);
|
||||
|
||||
// Ambient reflections
|
||||
float fakeLysSpecularPower=RoughToSPow(orgRoughness);
|
||||
float lysMipMap = GetSpecPowToMip(fakeLysSpecularPower,8);
|
||||
color += textureCube(skybox, r, lysMipMap).rgb * Fresnel2(substance,NdotV,orgRoughness);
|
||||
|
||||
// use this if you want a cheaper reflection cubemap calculation. If so, remember to remove the GetSpecPowToMip() and RoughToSPow()
|
||||
// color += textureCube(skybox, r,8.0 - (8.0*(1.0-orgRoughness))).rgb * reflectionFresnel;
|
||||
|
||||
// Ambient light
|
||||
color += textureCube(skybox, n, 7.0).rgb * albedo * (1.0 - Fresnel2(substance,NdotV,orgRoughness));
|
||||
|
||||
if(nLightCount > 0) {
|
||||
//light
|
||||
const int i = 0;
|
||||
// float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
// float NdotL = clamp(dot(n,light[i]) + 0.2,0.0, 1.0);
|
||||
// fakes disc lights up close to a star.
|
||||
float falloff = 1.0 / (0.25 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float NdotL = clamp(dot(n,light[i]) + 0.2,0.0, 1.0);
|
||||
|
||||
float intensity = falloff * NdotL;
|
||||
|
||||
vec3 L=light[i];
|
||||
L = normalize(L);
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
|
||||
float HdotN = clamp(dot(halfVec,n), 0.0, 0.98);
|
||||
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = D_GGX(HdotN, roughnessV);
|
||||
float V = V_SchlickforGGX(roughnessV, NdotV, NdotL);
|
||||
|
||||
color += ao * ((D * V * F) + (1.0 - F) * NdotL * albedo) * gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
}
|
||||
|
||||
if(nLightCount > 1) {
|
||||
//light
|
||||
const int i = 1;
|
||||
// float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
// float NdotL = clamp(dot(n,light[i]) + 0.2,0.0, 1.0);
|
||||
// fakes disc lights up close to a star.
|
||||
float falloff = 1.0 / (0.25 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float NdotL = clamp(dot(n,light[i]) + 0.2,0.0, 1.0);
|
||||
|
||||
float intensity = falloff * NdotL;
|
||||
|
||||
vec3 L=light[i];
|
||||
L = normalize(L);
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
|
||||
float HdotN = clamp(dot(halfVec,n), 0.0, 1.0);
|
||||
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = D_GGX(HdotN, roughnessV);
|
||||
float V = V_SchlickforGGX(roughnessV, NdotV, NdotL);
|
||||
|
||||
color += ao * ((D * V * F) + (1.0 - F) * NdotL * albedo) * gl_LightSource[i].diffuse.rgb * intensity;
|
||||
}
|
||||
gl_FragColor.rgb = color + lights;
|
||||
gl_FragColor.a = transparency + 1.0;
|
||||
}
|
||||
@@ -0,0 +1,470 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
// pirate
|
||||
//const vec3 colorLightsPrimary = vec3(0.949, 0.424, 0.31);
|
||||
//const vec3 colorLightsSecondary = vec3(0.929, 0.091, 0.041);
|
||||
//// certain plate parts are made brighter and more shiny.
|
||||
//const vec4 playerPlateProfile = vec4(0.98,0.60,0.52,0.9);
|
||||
|
||||
|
||||
//// remnant
|
||||
const vec3 colorLightsPrimary = vec3(0.31, 0.749, 0.624);
|
||||
const vec3 colorLightsSecondary = vec3(0.041, 0.749, 0.929);
|
||||
|
||||
// certain plate parts are made brighter and more shiny.
|
||||
const vec4 playerPlateProfile = vec4(0.48, 0.45, 0.42, 0.45);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform float acceleration;
|
||||
uniform float velocity;
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 vertMasks;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = (mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * scaleBias.r - scaleBias.g) * (1.0 - vertMasks.b);
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow(nodeScale, 0.5)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasks.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasks.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + maskSamp.b * 0.25 + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo * (maskSamp.b * 0.5 + 0.5));
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
// Engine setup
|
||||
float thrust = clamp((velocity * acceleration * 0.025), 0.0, 1.0);
|
||||
vec3 engineColor = (blackBody(1000.0 + 6000.0 * square(thrust)));
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * engineColor * (thrust + 0.1);
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * colorLightsPrimary;
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao
|
||||
color *= transAoPlatesMetal.g;
|
||||
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// engines are added
|
||||
if (advancedProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 4)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasks.b;
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 2)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasks.b;
|
||||
}
|
||||
else{
|
||||
color += engineColor * vertMasks.b;
|
||||
}
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
// pirate
|
||||
const vec3 colorLightsPrimary = vec3(0.949, 0.824, 0.231);
|
||||
const vec3 colorLightsSecondary = vec3(0.229, 0.691, 0.941);
|
||||
// certain plate parts are made brighter and more shiny.
|
||||
const vec4 playerPlateProfile = vec4(0.97,0.96,0.91,0.20);
|
||||
const vec4 ownerColor = vec4(0.88, 0.86, 0.90, 1.0);
|
||||
|
||||
//// remnant
|
||||
//const vec3 colorLightsPrimary = vec3(0.31, 0.749, 0.624);
|
||||
//const vec3 colorLightsSecondary = vec3(0.041, 0.749, 0.929);
|
||||
//
|
||||
//// certain plate parts are made brighter and more shiny.
|
||||
//const vec4 playerPlateProfile = vec4(0.48, 0.45, 0.42, 0.45);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform float acceleration;
|
||||
uniform float velocity;
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 vertMasks;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = ((mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * scaleBias.r - scaleBias.g) * (1.0 - vertMasks.b));
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow(nodeScale, 0.5)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasks.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasks.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + maskSamp.b * 0.25 + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
// Engine setup
|
||||
float thrust = clamp((velocity * acceleration * 0.025), 0.0, 1.0);
|
||||
vec3 engineColor = (blackBody(1000.0 + 6000.0 * square(thrust)));
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * engineColor * (thrust + 0.1);
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * colorLightsPrimary * (1.0 - floor(vertMasks.b + 0.1));
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao
|
||||
color *= transAoPlatesMetal.g;
|
||||
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// engines are added
|
||||
if (advancedProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 4)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasks.b;
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 2)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasks.b;
|
||||
}
|
||||
else{
|
||||
color += engineColor * vertMasks.b;
|
||||
}
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// shield color hardcoded to match the ship shield effect
|
||||
const vec3 shieldColor = vec3(0.3, 0.9,1.0);
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform float acceleration;
|
||||
uniform float velocity;
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec2 vertLightMask;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = (mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * scaleBias.r - scaleBias.g) * (1.0 - vertMasksSecondary.b);
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow(nodeScale, 0.5)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasksSecondary.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasksPrimary.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// player color setup
|
||||
vec4 playerPlateProfile = vec4(vec3(min(vec3(1.0), (1.0 - ownerColor.rgb) * 0.15 + 0.85)), 0.45);
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.85 + 0.15;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.85 + 0.15;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
// Engine setup
|
||||
float thrust = clamp((velocity * acceleration * 0.025), 0.0, 1.0);
|
||||
vec3 engineColor = (blackBody(1000.0 + 6000.0 * square(thrust)));
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * (1.0 - vertMasksPrimary.r) * colorLightsWindows;
|
||||
lights += uniqueEmissives.b * engineColor * (thrust + 0.1) * vertMasksPrimary.r;
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * vertLightMask.r * colorLightsPrimary;
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
detailLights += (vertMasksSecondary.g * lightMask.x) * colorLightsWindows * vertLightMask.g;
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao
|
||||
color *= transAoPlatesMetal.g;
|
||||
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// engines are added
|
||||
if (advancedProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 4)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasksSecondary.b;
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
color += engineColor * (max(0.0, fbm3D(vec3(origo.x * nodeScale * 0.25 + t.y, origo.yz * 64.0 * pow(nodeScale, 0.125)), 2)) * min(velocity, thrust + 0.1) + lightMask.x) * vertMasksSecondary.b;
|
||||
}
|
||||
else{
|
||||
color += engineColor * vertMasksSecondary.b;
|
||||
}
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec4 in_tangent;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
uniform mat3 invView;
|
||||
|
||||
varying vec3 forward, right;
|
||||
varying vec3 npos;
|
||||
varying vec3 normal, binormal, tangent;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 vertPaint;
|
||||
|
||||
void main()
|
||||
{
|
||||
normal = normalize(gl_NormalMatrix * in_normal);
|
||||
tangent = normalize(gl_NormalMatrix * in_tangent.xyz);
|
||||
binormal = normalize(gl_NormalMatrix * cross(normal, in_tangent.xyz * in_tangent.w));
|
||||
|
||||
forward = normalize(invView * vec3(1.0,0.0,0.0));
|
||||
right = normalize(invView * vec3(0.0,0.0,1.0));
|
||||
|
||||
vec4 pos = gl_ModelViewMatrix * in_vertex;
|
||||
npos = -pos.xyz;
|
||||
|
||||
vertPaint = in_color;
|
||||
|
||||
uv = in_uv;
|
||||
uv.y = 1.0 - uv.y;
|
||||
uv2 = in_uv2.xy;
|
||||
uv2.y = 1.0 - uv2.y;
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.y = 1.0 - uv3.y;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// shield color hardcoded to match the ship shield effect
|
||||
const vec3 shieldColor = vec3(0.3, 0.9,1.0);
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec2 vertLightMask;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// shield effect
|
||||
float shieldEffect(vec2 uv, vec3 t)
|
||||
{
|
||||
vec2 uvRad = uv - 0.5;
|
||||
float falloff = dot(uvRad, uvRad) * 3.0;
|
||||
if (simpleProcedurals){
|
||||
//source intensity levels
|
||||
vec2 pulse = 2.0 * uv + 2.0;
|
||||
pulse.x *= 2.0;
|
||||
pulse *= 4.0;
|
||||
|
||||
//frequency of radial ring ripples
|
||||
float radialRipples = sin(pulse.x + sin(t.x + pulse.x * falloff)) * sin(pulse.y + sin(t.x * 0.5));
|
||||
|
||||
float shield = 0.0;
|
||||
shield = radialRipples;
|
||||
pulse *= 0.5;
|
||||
pulse = abs(pulse);
|
||||
pulse *= falloff;
|
||||
pulse.x += sin(pulse.y * sin(pulse.x));
|
||||
|
||||
//frequency of ring ripples
|
||||
float ripples = sin(pulse.x + t.x);
|
||||
|
||||
shield += ripples;
|
||||
shield *= ripples * radialRipples;
|
||||
shield = max(0.0,min(1.0,shield)) * 0.33;
|
||||
shield += falloff * 0.33;
|
||||
|
||||
//end intensity levels
|
||||
shield *= 4.0 * falloff;
|
||||
return shield;
|
||||
}
|
||||
else{
|
||||
return falloff;
|
||||
}
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
// add spin to ring
|
||||
uvP.y += time * vertMasksPrimary.b * 64.0;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = (mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * scaleBias.r - scaleBias.g);
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
// create shields - detail level is inclueded in the subfunction
|
||||
float shields = shieldEffect(uv2, t) * vertMasksSecondary.r;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow5(nodeScale)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasksSecondary.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasksPrimary.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// player color setup
|
||||
vec4 playerPlateProfile = vec4(vec3(min(vec3(1.0), (1.0 - ownerColor.rgb) * 0.15 + 0.85)), 0.45);
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.85 + 0.15;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.85 + 0.15;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * (1.0 - vertMasksPrimary.r) * colorLightsWindows;
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * vertLightMask.r * colorLightsPrimary;
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
detailLights += (vertMasksSecondary.g * lightMask.x) * colorLightsWindows * vertLightMask.g;
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
if (simpleProcedurals){
|
||||
// makes shields flicker if area is damaged
|
||||
shields = mix(shields, abs(shields * sin(t.y) * sin(t.z)), damageMask.r);
|
||||
}
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao, max to avoid occluding things to black
|
||||
color *= max(0.1, transAoPlatesMetal.g);
|
||||
|
||||
// shading is revomed from the shields and lights are added
|
||||
color *= (1.0 - vertMasksSecondary.r);
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// shields are added
|
||||
color += mix(shieldColor, vec3(1.0), shields * 0.5) * vertMasksSecondary.r;
|
||||
|
||||
// shields transparency is set
|
||||
transAoPlatesMetal.r = 1.0 - vertMasksSecondary.r;
|
||||
transAoPlatesMetal.r += clamp(shields, 0.0, 1.0);
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = transAoPlatesMetal.r;
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// shield color hardcoded to match the ship shield effect
|
||||
const vec3 shieldColor = vec3(0.3, 0.9,1.0);
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec2 vertLightMask;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// shield effect
|
||||
float shieldEffect(vec2 uv, vec3 t)
|
||||
{
|
||||
vec2 uvRad = uv - 0.5;
|
||||
float falloff = dot(uvRad, uvRad) * 3.0;
|
||||
if (simpleProcedurals){
|
||||
//source intensity levels
|
||||
vec2 pulse = 2.0 * uv + 2.0;
|
||||
pulse.x *= 2.0;
|
||||
pulse *= 4.0;
|
||||
|
||||
//frequency of radial ring ripples
|
||||
float radialRipples = sin(pulse.x + sin(t.x + pulse.x * falloff)) * sin(pulse.y + sin(t.x * 0.5));
|
||||
|
||||
float shield = 0.0;
|
||||
shield = radialRipples;
|
||||
pulse *= 0.5;
|
||||
pulse = abs(pulse);
|
||||
pulse *= falloff;
|
||||
pulse.x += sin(pulse.y * sin(pulse.x));
|
||||
|
||||
//frequency of ring ripples
|
||||
float ripples = sin(pulse.x + t.x);
|
||||
|
||||
shield += ripples;
|
||||
shield *= ripples * radialRipples;
|
||||
shield = max(0.0,min(1.0,shield)) * 0.33;
|
||||
shield += falloff * 0.33;
|
||||
|
||||
//end intensity levels
|
||||
shield *= 4.0 * falloff;
|
||||
return shield;
|
||||
}
|
||||
else{
|
||||
return falloff;
|
||||
}
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
// make star beam animation
|
||||
float beamAnimation = noise3D(origo * vec3(4.0, 64.0, 64.0) - vec3(t.x, t.b,t.b));
|
||||
float beamMask = 1.0 - vertMasksPrimary.b;
|
||||
if (beamAnimation + (beamMask) < 0.33)
|
||||
discard;
|
||||
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = (mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * (scaleBias.r - scaleBias.g) * (1.0 - vertMasksSecondary.b));
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy * vec2(0.5, 1.0));
|
||||
float beamEmissives = texture2D(emissives, uv3.xy * vec2(0.5, 1.0) + vec2(0.5, 0.0)).r;
|
||||
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
// create shields - detail level is inclueded in the subfunction
|
||||
float shields = shieldEffect(uv2, t) * vertMasksSecondary.r;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow5(nodeScale)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasksSecondary.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasksPrimary.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// player color setup
|
||||
vec4 playerPlateProfile = vec4(vec3(min(vec3(1.0), (1.0 - ownerColor.rgb) * 0.15 + 0.85)), 0.45);
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.85 + 0.15;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.85 + 0.15;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * (1.0 - vertMasksPrimary.r) * colorLightsWindows;
|
||||
lights += square(min(0.9, beamEmissives)) * gl_LightSource[0].diffuse.rgb ;
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// tweak secondary colors accordin to red og blue dominated star
|
||||
vec3 beamColor = toLinear(gl_LightSource[0].diffuse.rgb);
|
||||
if (gl_LightSource[0].diffuse.r > gl_LightSource[0].diffuse.b)
|
||||
beamColor.gb *= beamAnimation * 0.25 + 0.25;
|
||||
else
|
||||
beamColor.rg *= beamAnimation * 0.25 + 0.25;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * vertLightMask.r * colorLightsPrimary;
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
detailLights += (vertMasksSecondary.g * lightMask.x) * colorLightsWindows * vertLightMask.g;
|
||||
detailLights += vertMasksSecondary.b * lightMask.x * beamColor;
|
||||
|
||||
lights += detailLights;
|
||||
// mix in beam
|
||||
lights = mix(lights, beamColor, vertMasksPrimary.b);
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
if (simpleProcedurals){
|
||||
// makes shields flicker if area is damaged
|
||||
shields = mix(shields, abs(shields * sin(t.y) * sin(t.z)), damageMask.r);
|
||||
}
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao, max to avoid occluding things to black
|
||||
color *= max(0.1, transAoPlatesMetal.g);
|
||||
|
||||
// shading is revomed from the shields and lights are added
|
||||
color *= (1.0 - vertMasksSecondary.r);
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// shields are added
|
||||
color += mix(shieldColor, vec3(1.0), shields * 0.5) * vertMasksSecondary.r;
|
||||
|
||||
// shields transparency is set
|
||||
transAoPlatesMetal.r = 1.0 - vertMasksSecondary.r;
|
||||
transAoPlatesMetal.r += clamp(shields, 0.0, 1.0);
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = transAoPlatesMetal.r;
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// shield color hardcoded to match the ship shield effect
|
||||
const vec3 shieldColor = vec3(0.3, 0.9,1.0);
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec2 vertLightMask;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// shield effect
|
||||
float shieldEffect(vec2 uv, vec3 t)
|
||||
{
|
||||
vec2 uvRad = uv - 0.5;
|
||||
float falloff = dot(uvRad, uvRad) * 3.0;
|
||||
if (simpleProcedurals){
|
||||
//source intensity levels
|
||||
vec2 pulse = 2.0 * uv + 2.0;
|
||||
pulse.x *= 2.0;
|
||||
pulse *= 4.0;
|
||||
|
||||
//frequency of radial ring ripples
|
||||
float radialRipples = sin(pulse.x + sin(t.x + pulse.x * falloff)) * sin(pulse.y + sin(t.x * 0.5));
|
||||
|
||||
float shield = 0.0;
|
||||
shield = radialRipples;
|
||||
pulse *= 0.5;
|
||||
pulse = abs(pulse);
|
||||
pulse *= falloff;
|
||||
pulse.x += sin(pulse.y * sin(pulse.x));
|
||||
|
||||
//frequency of ring ripples
|
||||
float ripples = sin(pulse.x + t.x);
|
||||
|
||||
shield += ripples;
|
||||
shield *= ripples * radialRipples;
|
||||
shield = max(0.0,min(1.0,shield)) * 0.33;
|
||||
shield += falloff * 0.33;
|
||||
|
||||
//end intensity levels
|
||||
shield *= 4.0 * falloff;
|
||||
return shield;
|
||||
}
|
||||
else{
|
||||
return falloff;
|
||||
}
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = (mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * (scaleBias.r - scaleBias.g) * (1.0 - vertMasksSecondary.b));
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
// create shields - detail level is inclueded in the subfunction
|
||||
float shields = shieldEffect(uv2, t) * vertMasksSecondary.r;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow5(nodeScale)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasksSecondary.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasksPrimary.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// player color setup
|
||||
vec4 playerPlateProfile = vec4(vec3(min(vec3(1.0), (1.0 - ownerColor.rgb) * 0.15 + 0.85)), 0.45);
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.85 + 0.15;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.85 + 0.15;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += vec3(uniqueEmissives.b * uniqueEmissives.b * vec3(0.223, 0.036, 0.343)) * (1.0 - vertMasksSecondary.b);
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * vertLightMask.r * colorLightsPrimary;
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
float warpColor = 0.0;
|
||||
// add warp storage effect
|
||||
if (simpleProcedurals){
|
||||
warpColor = square(1.0 - min(1.0,abs(noise3D(origo * 128.0 + time * 300.0) * 2.0 - 1.0)));
|
||||
}
|
||||
else{
|
||||
warpColor = square(NdotV);
|
||||
}
|
||||
detailLights += min(vec3(0.99), (mix(vec3(0.223, 0.036, 0.343), vec3(0.447, 0.071, 0.686), warpColor) + warpColor * warpColor * 0.1) * vertMasksSecondary.b);
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
if (simpleProcedurals){
|
||||
// makes shields flicker if area is damaged
|
||||
shields = mix(shields, abs(shields * sin(t.y) * sin(t.z)), damageMask.r);
|
||||
}
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao, max to avoid occluding things to black
|
||||
color *= max(0.1, transAoPlatesMetal.g);
|
||||
|
||||
// shading is revomed from the shields and lights are added
|
||||
color *= (1.0 - vertMasksSecondary.r);
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// shields are added
|
||||
color += mix(shieldColor, vec3(1.0), shields * 0.5) * vertMasksSecondary.r;
|
||||
|
||||
// shields transparency is set
|
||||
transAoPlatesMetal.r = 1.0 - vertMasksSecondary.r;
|
||||
transAoPlatesMetal.r += clamp(shields, 0.0, 1.0);
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = transAoPlatesMetal.r;
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// shield color hardcoded to match the ship shield effect
|
||||
const vec3 shieldColor = vec3(0.3, 0.9,1.0);
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives, biome;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec2 vertLightMask;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
float hex(vec2 p)
|
||||
{
|
||||
p = fract(p);
|
||||
float s = 1.5;
|
||||
p.x *= s;
|
||||
float hex = length(p - vec2(0.5 * s, 0.5));
|
||||
hex = min(hex, length(p - vec2(0.0, 0.0)));
|
||||
hex = min(hex, length(p - vec2( s, 0.0)));
|
||||
hex = min(hex, length(p - vec2(0.0, 1.0)));
|
||||
hex = min(hex, length(p - vec2( s, 1.0)));
|
||||
return hex;
|
||||
}
|
||||
|
||||
// shield effect
|
||||
float shieldEffect(vec2 uv)
|
||||
{
|
||||
vec2 uvRad = uv - 0.5;
|
||||
float falloff = dot(uvRad, uvRad);
|
||||
float h = hex(uv * 8.0);
|
||||
h = pow5(h) * 3.0;
|
||||
return falloff + h;
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
// adjust planet surface to terran biome
|
||||
uvP = mix(uvP, (uv * vec2(0.25, 0.5) - vec2(0.5, 0.0)), vertMasksPrimary.b);
|
||||
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = mix(mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r), texture2D(biome, uvP, 0.0).a, vertMasksPrimary.b) * scaleBias.r - scaleBias.g;
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
// sample habitat planet surface
|
||||
vec4 biomeSamp = texture2D(biome, uvP);
|
||||
|
||||
// create hex shields
|
||||
float shields = shieldEffect(uv2) * vertMasksSecondary.r;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow5(nodeScale)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasksSecondary.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasksPrimary.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// player color setup
|
||||
vec4 playerPlateProfile = vec4(vec3(min(vec3(1.0), (1.0 - ownerColor.rgb) * 0.15 + 0.85)), 0.45);
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.85 + 0.15;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.85 + 0.15;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
|
||||
// add planet texture to habitat surface
|
||||
orgRoughness = mix(orgRoughness, biomeSamp.b, vertMasksPrimary.b);
|
||||
biomeSamp.b *= 0.5;
|
||||
biomeSamp.b += 0.5;
|
||||
albedo = mix(albedo, mix(vec3(0.216, 0.29, 0.212), vec3(0.185, 0.335, 0.153), biomeSamp.a) * biomeSamp.a, vertMasksPrimary.b);
|
||||
transAoPlatesMetal = mix(transAoPlatesMetal, vec4(1.0,1.0,1.0,0.0), vertMasksPrimary.b);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
// mix with planet surface
|
||||
normEmissive.xy = mix(normEmissive.xy, 1.0 - biomeSamp.xy, vertMasksPrimary.b);
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * (1.0 - vertMasksPrimary.r) * colorLightsWindows;
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * vertLightMask.r * colorLightsPrimary;
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
detailLights += (vertMasksSecondary.g * lightMask.x) * colorLightsWindows * vertLightMask.g;
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
lights *= 1.0 - vertMasksPrimary.b;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
if (simpleProcedurals){
|
||||
// makes shields flicker if area is damaged
|
||||
shields = mix(shields, abs(shields * sin(t.y) * sin(t.z)), damageMask.r);
|
||||
}
|
||||
|
||||
maskSamp.b = mix(maskSamp.b, biomeSamp.x, vertMasksPrimary.b);
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao, max to avoid occluding things to black
|
||||
color *= max(0.1, transAoPlatesMetal.g);
|
||||
|
||||
// shading is revomed from the shields and lights are added
|
||||
color *= (1.0 - vertMasksSecondary.r);
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// shields are added
|
||||
color += mix(shieldColor, vec3(1.0), shields * 0.5) * vertMasksSecondary.r;
|
||||
|
||||
// shields transparency is set
|
||||
transAoPlatesMetal.r = 1.0 - vertMasksSecondary.r;
|
||||
transAoPlatesMetal.r += clamp(shields, 0.0, 1.0);
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = transAoPlatesMetal.r;
|
||||
}
|
||||
@@ -0,0 +1,506 @@
|
||||
#version 120
|
||||
// shader level settings
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
const int nLightCount = 2;
|
||||
|
||||
// math constants
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530716;
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 5.0;
|
||||
const float lightIntensity = 5.0;
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025); // vertmask flattens parallax on engines to avoid mirror artifacts
|
||||
|
||||
// shield color hardcoded to match the ship shield effect
|
||||
const vec3 shieldColor = vec3(0.3, 0.9,1.0);
|
||||
|
||||
// for procedural noise
|
||||
const vec4 hashSeed = vec4(.16532,.17369,.15787, .14987);
|
||||
|
||||
uniform sampler2D diffuse, normals, masks, damaged, emissives;
|
||||
uniform samplerCube skybox;
|
||||
uniform vec4 ownerColor;
|
||||
uniform float camDist;
|
||||
uniform float time, nodeScale;
|
||||
uniform float lightRadius[nLightCount];
|
||||
//damage direction top, right, bottom, left
|
||||
uniform vec4 damage;
|
||||
varying vec3 light[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
varying vec3 vertMasksPrimary, vertMasksSecondary;
|
||||
varying vec3 normal;
|
||||
varying vec3 npos, origo;
|
||||
varying vec2 uv, uv2, uv3;
|
||||
varying vec4 pos;
|
||||
varying vec2 vertLightMask;
|
||||
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
float pow32(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// unpacks two-channel in one channel textures
|
||||
vec2 unPack(float single) {
|
||||
|
||||
vec2 split = vec2(0.0);
|
||||
split.x = max(0.0, min(0.5, single) -0.25) * 2.0;
|
||||
split.y = 1.0 - ((single - split.x) * 4.0);
|
||||
split.x *= 2.0;
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// engine color sub function
|
||||
float mixRange(float x, float low, float hi) {
|
||||
return clamp((x - low) / (hi - low), 0.0, 1.0);
|
||||
}
|
||||
// engine color
|
||||
vec3 blackBody(float temp) {
|
||||
vec3 c;
|
||||
c.r = mix(1.0, 0.6234, mixRange(temp, 6400.0, 29800.0));
|
||||
c.b = mix(0.0, 1.0, mixRange(temp, 2800.0, 7600.0));
|
||||
c.g = mix(mix(0.22, 0.976, mixRange(temp, 1000.0, 6600.0)), mix(0.976, 0.65, mixRange(temp, 6600.0, 29800.0)), floor(temp/29800.0 + 0.22148));
|
||||
return c;
|
||||
}
|
||||
|
||||
// shield effect
|
||||
float shieldEffect(vec2 uv, vec3 t)
|
||||
{
|
||||
vec2 uvRad = uv - 0.5;
|
||||
float falloff = dot(uvRad, uvRad) * 3.0;
|
||||
if (simpleProcedurals){
|
||||
//source intensity levels
|
||||
vec2 pulse = 2.0 * uv + 2.0;
|
||||
pulse.x *= 2.0;
|
||||
pulse *= 4.0;
|
||||
|
||||
//frequency of radial ring ripples
|
||||
float radialRipples = sin(pulse.x + sin(t.x + pulse.x * falloff)) * sin(pulse.y + sin(t.x * 0.5));
|
||||
|
||||
float shield = 0.0;
|
||||
shield = radialRipples;
|
||||
pulse *= 0.5;
|
||||
pulse = abs(pulse);
|
||||
pulse *= falloff;
|
||||
pulse.x += sin(pulse.y * sin(pulse.x));
|
||||
|
||||
//frequency of ring ripples
|
||||
float ripples = sin(pulse.x + t.x);
|
||||
|
||||
shield += ripples;
|
||||
shield *= ripples * radialRipples;
|
||||
shield = max(0.0,min(1.0,shield)) * 0.33;
|
||||
shield += falloff * 0.33;
|
||||
|
||||
//end intensity levels
|
||||
shield *= 4.0 * falloff;
|
||||
return shield;
|
||||
}
|
||||
else{
|
||||
return falloff;
|
||||
}
|
||||
}
|
||||
|
||||
// random noise functions ahead
|
||||
float hash11(float p)
|
||||
{
|
||||
vec2 p2 = fract(vec2(p) * hashSeed.x);
|
||||
p2 += dot(p2.yx, p2.xy+19.19);
|
||||
return fract(p2.x * p2.y);
|
||||
}
|
||||
|
||||
float noise3D(vec3 x )
|
||||
{
|
||||
vec3 p = floor(x);
|
||||
vec3 f = fract(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return mix(mix(mix( hash11(n+ 0.0), hash11(n+ 1.0),f.x),
|
||||
mix( hash11(n+157.0), hash11(n+158.0),f.x),f.y),
|
||||
mix(mix( hash11(n+113.0), hash11(n+114.0),f.x),
|
||||
mix( hash11(n+270.0), hash11(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float fbm3D(vec3 n, int iterations) {
|
||||
float total = 0.0, amplitude = 0.66;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
total += noise3D(n) * amplitude;
|
||||
n += n;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// time used for shields and engines
|
||||
vec3 t = time * vec3(500.0, 250.0, 134.7);
|
||||
|
||||
//Damage setup
|
||||
float damageNoise = 0.0;
|
||||
if (advancedProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 3);
|
||||
}
|
||||
else if (simpleProcedurals){
|
||||
damageNoise = fbm3D(origo * 20.0, 2);
|
||||
}
|
||||
else{
|
||||
damageNoise = fbm3D(origo * 20.0, 1);
|
||||
}
|
||||
vec4 damageMask = clamp((vec4(-origo.z, origo.x, origo.z, -origo.x) * 8.0 + vec4(0.5)), vec4(0.0), vec4(1.0)) * damage * 0.5;
|
||||
damageMask.r = square((min(0.9, (damageMask.r + damageMask.g + damageMask.b + damageMask.a) * damageNoise)));
|
||||
|
||||
vec2 uvP = uv;
|
||||
vec2 uvPaint = uv2.xy;
|
||||
vec3 v = normalize(npos);
|
||||
vec3 n = normalize(normal);
|
||||
// first NdotV - used for parallax and low shading
|
||||
float NdotV = max(0.0, dot(normal, v));
|
||||
mat3 TBN = mat3(0.0);
|
||||
|
||||
// part one of normal mapping
|
||||
if (normalMapping){
|
||||
// tbn screenspace cotangent derivative
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
vec4 duv1 = duv1Calc(vec4(uvP, uvPaint));
|
||||
vec4 duv2 = duv2Calc(vec4(uvP, uvPaint));
|
||||
|
||||
// solve the linear system
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// construct a scale-invariant frame
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBN = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// do parallax for both main uv and paint, so paint doesn't float about
|
||||
if(parallax){
|
||||
|
||||
float p = ((mix(texture2D(normals, uvP, 0.0).b, texture2D(damaged, uvP, 0.0).b, damageMask.r) * scaleBias.r - scaleBias.g) * (1.0 - vertMasksSecondary.b));
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec2 vProjVTex = (duv1.xy * vProjVScr.x + duv2.xy * vProjVScr.y);
|
||||
float vProjVTexZ = NdotV * p;
|
||||
|
||||
uvP += (vProjVTex.xy * vProjVTexZ);
|
||||
|
||||
vProjVTex = (duv1.zw * vProjVScr.x + duv2.zw * vProjVScr.y);
|
||||
|
||||
uvPaint += (vProjVTex.xy * vProjVTexZ);
|
||||
}
|
||||
}
|
||||
|
||||
// sample textures
|
||||
// base albedo colors, transparency/detail ao
|
||||
vec4 diffuseSamp = texture2D(diffuse, uvP);
|
||||
//normals xy, unused here(parallax height), detail primary/secondary/engines/windows lights
|
||||
vec4 normEmissive = texture2D(normals, uvP);
|
||||
// roughness, plates/metal, specular (and paint, though not here)
|
||||
vec3 maskSamp = texture2D(masks, uvP).rgb;
|
||||
// damage normal xy, unused here(damage height) and damage spec/alpha
|
||||
vec4 damSamp = texture2D(damaged, uvP);
|
||||
// global secondary-, primary- and engines/windows self illumination, and model ao
|
||||
vec4 uniqueEmissives = texture2D(emissives, uv3.xy);
|
||||
// paint sample
|
||||
float paintSamp = texture2D(masks, uvPaint).a;
|
||||
|
||||
// create shields - detail level is inclueded in the subfunction
|
||||
float shields = shieldEffect(uv2, t) * vertMasksSecondary.r;
|
||||
|
||||
//Zoom-out highlight setup
|
||||
float highlight = smoothstep(0.0, 500.0, max(0.0001, camDist - 1.0) / pow5(nodeScale)) * 0.2;
|
||||
|
||||
// start of texture unpack/creation/mixing
|
||||
vec4 transAoPlatesMetal = vec4(unPack(mix(diffuseSamp.a, damSamp.a, damageMask.r)), unPack(maskSamp.g));
|
||||
|
||||
// special discard based on vertex mask color specific for stations - NOT ships!! (or they will be full of holes:oP)
|
||||
transAoPlatesMetal.r = mix(transAoPlatesMetal.r, 1.0, vertMasksSecondary.r * 2.0);
|
||||
if (transAoPlatesMetal.r + (1.0 - vertMasksPrimary.r) < 1.0)
|
||||
discard;
|
||||
|
||||
// player color setup
|
||||
vec4 playerPlateProfile = vec4(vec3(min(vec3(1.0), (1.0 - ownerColor.rgb) * 0.15 + 0.85)), 0.45);
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsWindows = ownerColor.rgb * 0.85 + 0.15;
|
||||
vec3 colorLightsPrimary = vec3(ownerColor.brg) * 0.85 + 0.15;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
// create various masks
|
||||
transAoPlatesMetal.a *= 1.0 + damageMask.r * 0.5;
|
||||
transAoPlatesMetal.ga = clamp(transAoPlatesMetal.ga, vec2(0.0), vec2(1.0));
|
||||
float battleDamage = mix(1.0, square((1.0 - damSamp.b) * damSamp.b), damageMask.r);
|
||||
battleDamage *= battleDamage * (3.0 - 2.0 * battleDamage);
|
||||
vec3 albedo = mix(diffuseSamp.rgb, vec3(0.24, 0.33, 0.52) * diffuseSamp.rgb * mix(1.0, square(transAoPlatesMetal.g * damSamp.b) + 0.1, damageMask.r), damageMask.r);
|
||||
float paintMask = clamp(floor(paintSamp * battleDamage * transAoPlatesMetal.b * 2.0 - 0.5), 0.0, 1.0) * ((1.0 - maskSamp.b) * 0.66 + 0.33);
|
||||
transAoPlatesMetal.a *= 1.0 - paintMask;
|
||||
|
||||
// final roughness, albedo and substance
|
||||
float orgRoughness = min(1.0, maskSamp.r * playerPlateProfile.a + damSamp.b * damageMask.r + (1.0 - battleDamage) * 0.25 + paintMask * 0.25);
|
||||
albedo = toLinear(mix(vec3(1.0), mix(playerPlateProfile.rgb, ownerColor.rgb, paintMask), transAoPlatesMetal.b) * albedo);
|
||||
vec3 substance = (0.04 - 0.04 * transAoPlatesMetal.a) + albedo * transAoPlatesMetal.a;
|
||||
albedo -= substance;
|
||||
|
||||
//0.51 to clean off bad splitting
|
||||
vec2 lightMask = square(max(vec2(0.0), vec2(1.0 - normEmissive.a, normEmissive.a) - 0.51) * 2.0);
|
||||
// end of texture unpack/creation/mixing
|
||||
|
||||
if (normalMapping){
|
||||
// mix with damage
|
||||
normEmissive.xy = mix(normEmissive.xy, damSamp.xy, damageMask.r);
|
||||
|
||||
// Normal setup
|
||||
normEmissive.xy *= 2.0;
|
||||
normEmissive.xy -= 1.0;
|
||||
n = normalize(TBN * normalize(vec3(normEmissive.xy, deriveZ(normEmissive.xy))));
|
||||
NdotV = max(0.0, dot(n, v));
|
||||
}
|
||||
// reflection vector
|
||||
vec3 r = normalize(reflect(-v, n));
|
||||
|
||||
// actual shading starts here
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV ,orgRoughness);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(orgRoughness) * 4.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 4.0).rgb + 0.024) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedo * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedo * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
vec3 lights = vec3(0.0);
|
||||
|
||||
if (selfIllumination){
|
||||
// self illuminate for primary, secondary, windows and engines are added.
|
||||
uniqueEmissives.rgb *= uniqueEmissives.rgb; // close to linear conversion, not the ao channel!
|
||||
uniqueEmissives.rgb *= (1.0 - (lightMask.x + lightMask.y));
|
||||
lights = uniqueEmissives.r * colorLightsPrimary;
|
||||
lights += uniqueEmissives.g * colorLightsSecondary;
|
||||
lights += uniqueEmissives.b * (1.0 - vertMasksPrimary.r) * colorLightsWindows;
|
||||
lights *= emissiveIntensity;
|
||||
|
||||
// Self-illumination fake pbr calculations.
|
||||
vec3 emissiveFresnel = mix((1.0 - NdotV) * substance, albedo, pow5(orgRoughness));
|
||||
lights *= emissiveFresnel;
|
||||
|
||||
// detail ao on self illumination, unaffected by model ao
|
||||
lights *= transAoPlatesMetal.g;
|
||||
|
||||
}
|
||||
// combine mode and detail ao for further use;
|
||||
transAoPlatesMetal.g *= uniqueEmissives.w;
|
||||
|
||||
// primary and secondary lights, windows and engines is added after fresnel calculations
|
||||
vec3 detailLights = lightMask.x * vertLightMask.r * colorLightsPrimary;
|
||||
detailLights += lightMask.y * colorLightsSecondary;
|
||||
detailLights += (vertMasksSecondary.g * lightMask.x) * colorLightsWindows * vertLightMask.g;
|
||||
|
||||
lights += detailLights;
|
||||
|
||||
// occlude lights in damaged areas
|
||||
lights *= pow5(1.0 - damageMask.r);
|
||||
|
||||
if (simpleProcedurals){
|
||||
// makes shields flicker if area is damaged
|
||||
shields = mix(shields, abs(shields * sin(t.y) * sin(t.z)), damageMask.r);
|
||||
}
|
||||
|
||||
if (pbrLight){
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec,n));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, orgRoughness));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + orgRoughness) * 0.5, NdotV, NdotL));
|
||||
float O = OrenNayerforDiffuseOnly(orgRoughness, NdotL, NdotV);
|
||||
|
||||
color += ((D * V * F) + (1.0 - F) * O * albedo) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= (maskSamp.b + 1.0);
|
||||
}
|
||||
// this light model is pretty loose in terms of what it does, design focus to match
|
||||
// the pbr in terms of visibility and overall feel for a fraction of the price
|
||||
else{
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n));
|
||||
vec3 S = Fresnel2(substance, HdotN ,orgRoughness);
|
||||
// albedo * (maskSamp.b * 0.5) to sorta hack albedo into a classical diffuse texture
|
||||
lights += (albedo * (maskSamp.b * 0.5) + pow(S * HdotN, vec3(maskSamp.b + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
// apply ao, max to avoid occluding things to black
|
||||
color *= max(0.1, transAoPlatesMetal.g);
|
||||
|
||||
// shading is revomed from the shields and lights are added
|
||||
color *= (1.0 - vertMasksSecondary.r);
|
||||
color += lights * emissiveIntensity;
|
||||
|
||||
// shields are added
|
||||
color += mix(shieldColor, vec3(1.0), shields * 0.5) * vertMasksSecondary.r;
|
||||
|
||||
// shields transparency is set
|
||||
transAoPlatesMetal.r = 1.0 - vertMasksSecondary.r;
|
||||
transAoPlatesMetal.r += clamp(shields, 0.0, 1.0);
|
||||
|
||||
// convert back to gamma space, add zoom highlight, and enjoy the show :o)
|
||||
gl_FragColor.rgb = toGamma(clamp(color, vec3(0.0), vec3(1.0))) + highlight;
|
||||
gl_FragColor.a = transAoPlatesMetal.r;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#define pi 3.141592653589793238462643383279
|
||||
#define twopi (pi * 2.0)
|
||||
varying vec2 rcoords;
|
||||
|
||||
const float maxRad = 0.9;
|
||||
const float minRad = 0.7;
|
||||
|
||||
void main() {
|
||||
float r = length(rcoords);
|
||||
vec4 color = gl_Color;
|
||||
if(r < minRad || r > maxRad)
|
||||
discard;
|
||||
|
||||
float alpha = abs(r - (minRad + (maxRad - minRad) / 2.0));
|
||||
alpha /= (maxRad - minRad);
|
||||
alpha = 1.0 - alpha;
|
||||
color.a *= alpha;
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
#version 120
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530717;
|
||||
uniform sampler2D surfaceData, biomes, lookup;
|
||||
varying vec2 uv[5], uvB;
|
||||
uniform vec2 cracksColorIntensity;
|
||||
uniform vec4[3] biomesPicks;
|
||||
uniform vec2 texSize;
|
||||
uniform vec2 fullSize;
|
||||
|
||||
varying vec2 blur;
|
||||
|
||||
// calculates palette for cracks
|
||||
vec3 paletteCracks(float c)
|
||||
{
|
||||
return 0.5 + 0.5 * cos(tau * (1.0 * c + vec3(0.0, 0.33, 0.67)) );
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
// pow alternatives
|
||||
float dotter(vec3 x) {
|
||||
return dot(x,x);
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec3 pow3(vec3 x) {
|
||||
return x*x*x;
|
||||
}
|
||||
|
||||
void main() {
|
||||
// clamp so we don't get south poles blending into north poles an vice versa
|
||||
|
||||
vec2 uvL;
|
||||
vec2 uvS = uv[0];
|
||||
vec2 surfUV = texSize / fullSize;
|
||||
vec2 surfMin = vec2(0.0, blur.y);
|
||||
vec2 surfMax = vec2(1.0, 1.0 - blur.y);
|
||||
if(uvS.x < surfUV.x) {
|
||||
surfMin.x = blur.x;
|
||||
surfMax.x = surfUV.x - blur.x * 0.5;
|
||||
}
|
||||
if(uvS.y < surfUV.y) {
|
||||
surfMin.y = blur.y;
|
||||
surfMax.y = surfUV.y - blur.y * 0.5;
|
||||
}
|
||||
|
||||
vec4 splatMap = texture2D(surfaceData, clamp(uvS, surfMin, surfMax));
|
||||
if(splatMap.a < 0.5 && splatMap.r < 0.5 && splatMap.g < 0.5)
|
||||
discard;
|
||||
|
||||
vec3 albedo = vec3(0.0);
|
||||
float cracks = 0.0;
|
||||
vec2 cracksWaterMask = vec2(0.0);
|
||||
|
||||
// make barren moon
|
||||
if(splatMap.a < 0.5 && splatMap.r > 0.5) {
|
||||
vec2 moonBiome = texture2D(biomes, uvB + vec2(0.25, 0.5)).ba;
|
||||
moonBiome.g = clamp((1.0 - moonBiome.g), 0.00390625, 0.984375);
|
||||
albedo += texture2D(lookup, vec2(0.4072265625, moonBiome.g)).rgb * (0.5 + moonBiome.r);
|
||||
}
|
||||
// else make terran continent 0.6103515625
|
||||
else if(splatMap.a < 0.5 && splatMap.g > 0.5) {
|
||||
vec2 continentBiome = texture2D(biomes, uvB + vec2(0.5, 0.5)).ba;
|
||||
continentBiome.g = clamp((1.0 - continentBiome.g), 0.00390625, 0.984375);
|
||||
albedo += texture2D(lookup, vec2(0.4072265625, continentBiome.g)).rgb * (0.5 + continentBiome.r);
|
||||
}
|
||||
// make regular biome
|
||||
else {
|
||||
|
||||
splatMap.rgb *= 2.0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uvL = uv[i+1];
|
||||
vec2 blurUV = clamp(uvL, surfMin, surfMax);
|
||||
splatMap.rgb += texture2D(surfaceData, blurUV).rgb;
|
||||
}
|
||||
splatMap.rgb *= 0.166666666;
|
||||
|
||||
cracksWaterMask = vec2((splatMap.b - 0.5) * 2.0);
|
||||
cracksWaterMask = max(vec2(0.0),vec2(-1.0 * cracksWaterMask.x, cracksWaterMask.y));
|
||||
|
||||
// need to sample xy normal channels
|
||||
vec2 firstBiome = texture2D(biomes, uvB + biomesPicks[0].xy).ba;
|
||||
vec2 secondBiome = texture2D(biomes, uvB + biomesPicks[1].xy).ba;
|
||||
vec2 thirdBiome = texture2D(biomes, uvB + biomesPicks[2].xy).ba;
|
||||
|
||||
vec3 heightSamps = vec3(firstBiome.g, secondBiome.g, thirdBiome.g);
|
||||
|
||||
vec2 splatMaskMixed = vec2(firstBiome.g + secondBiome.g, secondBiome.g + thirdBiome.g);
|
||||
splatMaskMixed = min(vec2(1.0), (splatMaskMixed + splatMap.rg) * splatMap.rg);
|
||||
|
||||
heightSamps = clamp((1.0 - heightSamps), vec3(0.00390625), vec3(0.984375));
|
||||
|
||||
// sample albedo, no need to sample roughness data
|
||||
vec3 firstBiomesAlbedoR = texture2D(lookup, vec2(biomesPicks[0].z, heightSamps.r)).rgb; //, 0.0
|
||||
vec3 secondBiomesAlbedoR = texture2D(lookup, vec2(biomesPicks[1].z, heightSamps.g)).rgb; //, 0.0
|
||||
vec3 thirdBiomesAlbedoR = texture2D(lookup, vec2(biomesPicks[2].z, heightSamps.b)).rgb; //, 0.0
|
||||
|
||||
vec2 dataSamp = mix(mix(firstBiome, secondBiome, splatMaskMixed.r), thirdBiome, splatMaskMixed.g);
|
||||
|
||||
float ao = min(1.0,(dataSamp.r * 2.0));
|
||||
|
||||
cracks = min(1.0, (1.0 - ao) * cracksWaterMask.r);
|
||||
|
||||
vec3 glow = pow3(paletteCracks(fract(cracks * 0.025 + cracksColorIntensity.x)) * cracks * cracksColorIntensity.y);
|
||||
|
||||
albedo += mix(mix(firstBiomesAlbedoR, secondBiomesAlbedoR, splatMaskMixed.r), thirdBiomesAlbedoR, splatMaskMixed.g);
|
||||
|
||||
albedo.rgb *= 0.25 + dataSamp.r;
|
||||
|
||||
float waterDepth = max(0.0, 1.0 - (dataSamp.g + square(cracksWaterMask.y)));
|
||||
vec3 oceanColor = texture2D(lookup, vec2(biomesPicks[0].z, max(0.995,waterDepth * 0.0025))).rgb * (waterDepth * 0.66 + 0.33); // from 0.0065 to 0.0025
|
||||
|
||||
|
||||
cracksWaterMask.y = max(0.0, ceil(dataSamp.g - cracksWaterMask.y));
|
||||
albedo = mix(oceanColor, albedo, cracksWaterMask.y) * (1.0 - cracks) + glow * 8.0;
|
||||
|
||||
}
|
||||
|
||||
gl_FragColor.rgb = albedo;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
varying vec2 uv[5], uvB;
|
||||
const vec2 fractions = vec2(0.984375, 0.0078125);
|
||||
uniform vec2 texSize;
|
||||
uniform vec2 fullSize;
|
||||
|
||||
varying vec2 blur;
|
||||
|
||||
void main() {
|
||||
// make biome tiler
|
||||
// to reuse the tiling planet surfaces we need to account for the bleed they have baked into the planet sphere uv's.
|
||||
// each planet tile is 2016x2016 with 16 pixel tiled bleed on each side
|
||||
uvB = (vec2(gl_Color.x, gl_MultiTexCoord0.y) * fractions.x + fractions.y) / vec2(4.0, 2.0);
|
||||
uvB.y = 1.0 - uvB.y;
|
||||
|
||||
// make splatmap blurs
|
||||
blur = vec2(1.0 / fullSize.x, 1.0 / fullSize.y);
|
||||
uv[0] = gl_MultiTexCoord0.xy;
|
||||
uv[1] = uv[0] + blur;
|
||||
uv[2] = uv[0] + vec2(-blur.x, blur.y);
|
||||
uv[3] = uv[0] + vec2(-blur.x, -blur.y);
|
||||
uv[4] = uv[0] + vec2(blur.x, -blur.y);
|
||||
|
||||
gl_Position = ftransform();
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_BackColor = gl_Color;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
|
||||
//Amount of population considered 'half full'
|
||||
const float basePopulation = 3.0;
|
||||
|
||||
const float distortDist = 5.0 / 2048.0;
|
||||
const float baseSpec = 0.5;
|
||||
|
||||
uniform sampler2D diffuseTex, glowTex, normalRGBspecA, cities, diffNoise;
|
||||
uniform float[4] uvOffsets;
|
||||
uniform float population;
|
||||
uniform vec3 glowGradient[2];
|
||||
|
||||
varying vec3 normal, binormal, tangent;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
vec2 nightLights(vec3 incidentLight, float devLevel) {
|
||||
float brightness = clamp(max(incidentLight.r, max(incidentLight.g, incidentLight.b)) * 0.9, 0.0, 1.0);
|
||||
brightness /= 0.4;
|
||||
|
||||
float popDensity = population / (population + basePopulation);
|
||||
float city = texture2D(cities, uv).r;
|
||||
float buildup = clamp((popDensity - (1.0 - devLevel)) * 2.0, 0.0, 1.0);
|
||||
|
||||
float lightFactor = sqrt(max(popDensity, 0.4));
|
||||
|
||||
return vec2(lightFactor * max(1.0 - brightness, 0.0) * buildup, max(buildup - 0.5, 0.0) * 2.0) * city;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 color = gl_FrontMaterial.diffuse.rgb;
|
||||
|
||||
vec3 diffuseSamp = texture2D(diffuseTex, uv).rgb;
|
||||
float glow = texture2D(glowTex, uv).r;
|
||||
vec4 normSpec = texture2D(normalRGBspecA, uv);
|
||||
float cityLevel = texture2D(cities, uv).g;
|
||||
|
||||
vec3 mapNorm = normSpec.xyz - vec3(0.5);
|
||||
float gloss = (length(mapNorm) - 0.25) / 0.25;
|
||||
float shininess = gl_FrontMaterial.shininess * (gloss + baseSpec);
|
||||
|
||||
vec3 n = normalize(normal) * mapNorm.z;
|
||||
n += normalize(binormal) * mapNorm.x;
|
||||
n += normalize(tangent) * mapNorm.y;
|
||||
n = normalize(n);
|
||||
vec3 v = normalize(npos);
|
||||
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
|
||||
vec3 ambient = gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;
|
||||
vec3 diffuse = vec3(0.0);
|
||||
vec3 specular = vec3(0.0);
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float intensity = max(0.0, dot(n, light[i])) * falloff;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
vec3 r = -reflect(light[i], n);
|
||||
specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float intensity = max(0.0, dot(n, light[i])) * falloff;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
vec3 r = -reflect(light[i], n);
|
||||
specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
|
||||
}
|
||||
specular *= gl_FrontMaterial.specular.rgb * (0.5 + gloss);
|
||||
|
||||
vec2 cityState = nightLights(diffuse, cityLevel);
|
||||
vec3 surfaceMix = ((diffuse + ambient) * color * mix(diffuseSamp.rgb, vec3(0.5), cityState.y));
|
||||
surfaceMix += specular;
|
||||
|
||||
if(glow > 0.01) {
|
||||
vec2 noiseCoord = uv * 4.0;
|
||||
vec2 noise2Coord = noiseCoord + vec2( uvOffsets[2] * -1.0, uvOffsets[3] );
|
||||
noiseCoord += vec2( uvOffsets[0], uvOffsets[1] );
|
||||
|
||||
noiseCoord.y = mod(noiseCoord.y, 1.0);
|
||||
noise2Coord.y = mod(noise2Coord.y, 1.0);
|
||||
|
||||
glow *= (0.85 + (texture2D(diffNoise, noiseCoord).b * 0.42)) * (0.85 + (texture2D(diffNoise, noise2Coord).r * 0.32));
|
||||
|
||||
gl_FragColor.rgb = surfaceMix + (glow * mix(glowGradient[0], glowGradient[1], glow)) + smoothstep(0.1,0.0,glow) * cityState.x * vec3(1.0,0.9,0.55);
|
||||
}
|
||||
else {
|
||||
gl_FragColor.rgb = surfaceMix + cityState.x * vec3(1.0,0.9,0.55);
|
||||
}
|
||||
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
attribute vec4 in_position;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
|
||||
varying vec3 npos;
|
||||
varying vec3 normal, binormal, tangent;
|
||||
varying vec2 uv;
|
||||
|
||||
uniform float flipState;
|
||||
|
||||
void main()
|
||||
{
|
||||
normal = normalize(gl_NormalMatrix * in_normal);
|
||||
binormal = normalize(cross(normal, gl_NormalMatrix * vec3(0.0,0.999,0.04471017781)));
|
||||
tangent = normalize(cross(normal, binormal));
|
||||
|
||||
vec4 pos = gl_ModelViewMatrix * in_position;
|
||||
npos = -pos.xyz;
|
||||
|
||||
vec2 baseUV = in_uv;
|
||||
if(flipState < 0.5) {
|
||||
if(flipState < 0.25) {
|
||||
baseUV.x = 1.0 - baseUV.x;
|
||||
binormal = -binormal;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(flipState < 0.75) {
|
||||
baseUV.y = 1.0 - baseUV.y;
|
||||
tangent = -tangent;
|
||||
}
|
||||
else {
|
||||
baseUV = vec2(1.0) - baseUV;
|
||||
binormal = -binormal;
|
||||
tangent = -tangent;
|
||||
}
|
||||
}
|
||||
uv = baseUV;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
const int nLightCount = 2;
|
||||
|
||||
uniform sampler2D diffuseRGBspecA;
|
||||
uniform sampler2D cities;
|
||||
uniform sampler2D lava;
|
||||
uniform float population;
|
||||
uniform float pctDestroyed;
|
||||
|
||||
//Amount of population considered 'half full'
|
||||
const float basePopulation = 8.0;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
vec3 nightLights(vec3 incidentLight) {
|
||||
float brightness = clamp(max(incidentLight.r, max(incidentLight.g, incidentLight.b)) * 0.9, 0.0, 1.0);
|
||||
if(brightness > 0.4)
|
||||
return vec3( 0.0 );
|
||||
brightness /= 0.4;
|
||||
|
||||
float popDensity = population / basePopulation;
|
||||
float level = texture2D(cities, uv).r;
|
||||
|
||||
return vec3( level * clamp(level - 1.0 + popDensity, 0.0, 1.0) * clamp(level - brightness, 0.0, 1.0) );
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 texSamp = texture2D(diffuseRGBspecA, uv);
|
||||
vec3 matspec = gl_FrontMaterial.specular.rgb * texSamp.a;
|
||||
float shininess = gl_FrontMaterial.shininess * (0.5 + texSamp.a);
|
||||
|
||||
vec3 n = normalize(normal);
|
||||
vec3 v = normalize(npos);
|
||||
|
||||
vec3 diffuse = vec3(0);
|
||||
vec3 specular = vec3(0);
|
||||
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
float intensity = max(0.0, dot(n, light[i]));
|
||||
if(intensity > 0.0) {
|
||||
//Apply falloff
|
||||
intensity /= (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
vec3 r = -reflect(light[i], n);
|
||||
specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
|
||||
}
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
float intensity = max(0.0, dot(n, light[i]));
|
||||
if(intensity > 0.0) {
|
||||
//Apply falloff
|
||||
intensity /= (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
vec3 r = -reflect(light[i], n);
|
||||
specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
|
||||
}
|
||||
}
|
||||
diffuse *= gl_FrontMaterial.diffuse.rgb;
|
||||
specular *= matspec;
|
||||
|
||||
vec3 ambient = gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;
|
||||
|
||||
vec3 rgb = ((diffuse + ambient) * texSamp.rgb) + specular + nightLights(diffuse);
|
||||
vec4 lavaSamp = texture2D(lava, uv);
|
||||
lavaSamp.a = clamp(lavaSamp.a - 1.0 + pctDestroyed, 0.0, 1.0);
|
||||
|
||||
gl_FragColor.rgb = mix(rgb, (lavaSamp.rgb * (diffuse + ambient)) * (pctDestroyed + lavaSamp.a) + specular, clamp(lavaSamp.a * 10.0,0.0,1.0));
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
#version 120
|
||||
const bool advancedProcedurals = #{{level:extreme}};
|
||||
const bool parallax = #{{level:extreme}};
|
||||
const bool scattering = #{{level:extreme}};
|
||||
const bool pbrLight = #{{level:high}};
|
||||
const bool simpleProcedurals = #{{level:high}};
|
||||
const bool advancedAmbience = #{{level:high}};
|
||||
const bool normalMapping = #{{level:medium}};
|
||||
const bool simpleAmbience = #{{level:medium}};
|
||||
const bool selfIllumination = #{{level:medium}};
|
||||
|
||||
|
||||
const int nLightCount = 2;
|
||||
const float pi = 3.14159265358;
|
||||
const float tau = 6.28318530717;
|
||||
|
||||
// constants for emissive lights and starlight intensity
|
||||
const float emissiveIntensity = 2.5;
|
||||
const float lightIntensity = 1.0;
|
||||
|
||||
uniform sampler2D biomes, cities, differenceNoise, lookup, cityGlow, surfaceData;
|
||||
uniform samplerCube skybox;
|
||||
uniform float lightRadius[nLightCount];
|
||||
uniform vec4 ownerColor;
|
||||
uniform float population;
|
||||
vec4 playerPlateProfile = vec4(vec3((1.0 - ownerColor.rgb) * 0.2 + 0.8), 0.75);
|
||||
|
||||
// complimentary triad color harmony, should in theory always generate an appealing theme.
|
||||
vec3 colorLightsPrimary = ownerColor.rgb * 0.45 + 0.55;
|
||||
vec3 colorLightsSecondary = vec3(ownerColor.brg) * 0.45 + 0.55;
|
||||
vec3 colorLightsTertiary = vec3(ownerColor.gbr) * 0.85 + 0.15;
|
||||
|
||||
const float splatSharpness = 2.0;
|
||||
|
||||
////Amount of population considered 'half full'
|
||||
const vec3 basePopulation = vec3(1.0, 0.25, 0.25);
|
||||
|
||||
varying vec2 uv, uv2, uv3, uvB[5];
|
||||
varying vec4 uvNoise, pos;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 lightColor[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
varying vec3 normal, npos, vertCol;
|
||||
varying float pulse;
|
||||
|
||||
/*
|
||||
black biome is base biome, its biome color picks are also what controls the ocean color picks
|
||||
red biome is secondary biome, that will splat on top of base
|
||||
green biome is third biome, that will splat on top, the poles are hardcoded to be green biome, so keep it the coldest biome pick wise.
|
||||
ocean and cracks a bonus biomes, so with both there can be 5 i total. Blue > 0.5 is ocean, blue < 0.5 is cracks. Ocean is using the combined height for detail, cracks is using ao to spawn in cracks.
|
||||
alpha is city location and density, it rules over all others but will be build under water.
|
||||
*/
|
||||
|
||||
//vec2(0.0, 0.0) vulcanic
|
||||
//vec2(0.25, 0.0) crystal
|
||||
//vec2(0.5, 0.0) mountains
|
||||
//vec2(0.75, 0.0) cracked
|
||||
//vec2(0.0, 0.5) ice
|
||||
//vec2(0.25, 0.5) barren
|
||||
//vec2(0.5, 0.5) terran
|
||||
//vec2(0.75, 0.5) desert
|
||||
|
||||
// x = 0-1 color wheel picker, y is intensity
|
||||
uniform vec2 cracksColorIntensity;
|
||||
uniform vec4[3] biomesPicks;
|
||||
|
||||
|
||||
// parallax scale, bias and steps
|
||||
const vec2 scaleBias = vec2(0.005, 0.0025);
|
||||
|
||||
// smoothstep without the edges
|
||||
float smoothCurve(float x){
|
||||
return x * x * (3.0 - 2.0 * x);
|
||||
}
|
||||
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 toGamma(vec3 x) {
|
||||
return pow(x, vec3(0.45));
|
||||
}
|
||||
|
||||
vec3 square(vec3 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float square(float x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
vec2 square(vec2 x) {
|
||||
return x*x;
|
||||
}
|
||||
|
||||
float pow5(float x) {
|
||||
|
||||
float y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
vec2 pow5(vec2 x) {
|
||||
|
||||
vec2 y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
vec3 pow5(vec3 x) {
|
||||
|
||||
vec3 y = x*x;
|
||||
return y*y*x;
|
||||
}
|
||||
|
||||
vec3 pow3(vec3 x) {
|
||||
return x*x*x;
|
||||
}
|
||||
|
||||
vec2 pow3(vec2 x) {
|
||||
return x*x*x;
|
||||
}
|
||||
|
||||
float pow3(float x) {
|
||||
return x*x*x;
|
||||
}
|
||||
|
||||
float pow8(float x) {
|
||||
|
||||
x = x*x;
|
||||
x = x*x;
|
||||
return x*x;
|
||||
}
|
||||
|
||||
// speculer term part 1
|
||||
float D_GGX(float HdotN, float Roughness)
|
||||
{
|
||||
float m = Roughness * Roughness;
|
||||
float m2 = m * m;
|
||||
float denominator = HdotN * HdotN * (m2 - 1.0) + 1.0;
|
||||
float D = m2 /( pi * denominator * denominator);
|
||||
return D;
|
||||
}
|
||||
|
||||
// specular term part 2
|
||||
float V_SchlickforGGX(float Roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float k = Roughness * Roughness * 0.5f;
|
||||
float G1V = NdotV * (1.0 - k) + k;
|
||||
float G1L = NdotL * (1.0 - k) + k;
|
||||
return 0.25f / (G1V * G1L);
|
||||
}
|
||||
// fresnel for specular term
|
||||
vec3 Fresnel(vec3 substance, vec3 l, vec3 h)
|
||||
{
|
||||
return max(vec3(0.0), substance + (substance - 1.0) * pow(1.0 - max(0.0,(dot(l, h))), 5.0));
|
||||
}
|
||||
// fresnel for ambient light
|
||||
vec3 Fresnel2(vec3 substance, float dotProduct, float roughness)
|
||||
{
|
||||
return substance + (1.0 - substance) * pow((1.0 - dotProduct), 5.0) / (6.0 - 5.0 * roughness);
|
||||
}
|
||||
// diffuse term
|
||||
float OrenNayerforDiffuseOnly(float roughness, float NdotL, float NdotV)
|
||||
{
|
||||
float O = 0.62 - pow(1.0-NdotL * clamp(1.0-NdotV/2.0, 0.0,1.0), pi) * 0.62;
|
||||
O = mix(O, NdotL, roughness);
|
||||
return O;
|
||||
}
|
||||
|
||||
// fail-safe get normal map z component
|
||||
float deriveZ(vec2 n) {
|
||||
|
||||
float z = pow(abs(1.0 - n.x * n.x - n.y * n.y), 0.5);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
// calculates palette for cracks
|
||||
vec3 paletteCracks(float c)
|
||||
{
|
||||
return 0.5 + 0.5 * cos(tau * (1.0 * c + vec3(0.0, 0.33, 0.67)) );
|
||||
}
|
||||
|
||||
// calculates light tinting, simulating atmospheric scattering.
|
||||
vec3 paletteAtmoTint( float t )
|
||||
{
|
||||
t *= t;
|
||||
return square(min(vec3(1.0), 0.45 + 0.45*cos( tau*((t * 0.9)+vec3(0.45, 0.55, 0.65)) ) + (1.0 - t) * 0.25));
|
||||
}
|
||||
|
||||
vec3 dp1Calc(vec3 p)
|
||||
{
|
||||
return dFdx(p);
|
||||
}
|
||||
|
||||
vec3 dp2Calc(vec3 p)
|
||||
{
|
||||
return dFdy(p);
|
||||
}
|
||||
|
||||
vec4 duv1Calc(vec4 uv)
|
||||
{
|
||||
return dFdx(uv);
|
||||
}
|
||||
|
||||
vec4 duv2Calc(vec4 uv)
|
||||
{
|
||||
return dFdy(uv);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 splatMap = texture2D(surfaceData, uvB[0]) * 2.0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
splatMap.rgb += texture2D(surfaceData, uvB[i+1]).rgb;
|
||||
}
|
||||
splatMap.rgb *= 0.166666666;
|
||||
float poleMask = smoothCurve(1.0 - square(vertCol.b));
|
||||
splatMap.rg *= poleMask;
|
||||
|
||||
// poles are made to be ruled by green, and water/cracks masks are made to never reach the poles - where they blend particular bad due to all splatmap grids meeing in a point
|
||||
splatMap.gb = mix(vec2(1.0, 0.5), splatMap.gb, poleMask);
|
||||
|
||||
vec2 cracksWaterMask = vec2((splatMap.b - 0.5) * 2.0);
|
||||
cracksWaterMask = max(vec2(0.0),vec2(-1.0 * cracksWaterMask.x, cracksWaterMask.y));
|
||||
vec4 uvS = vec4(uv, uv2);
|
||||
|
||||
vec3 v = normalize(npos);
|
||||
vec4 n = vec4(normalize(normal), 0.5); // heightmap is stored in a later
|
||||
vec2 NdotV = vec2(max(0.0, dot(n.xyz, v)), 0.0);
|
||||
|
||||
vec3 r = n.xyz;
|
||||
vec4 albedoR = vec4(0.0); // pure color of a surface and roughness
|
||||
vec3 substance = vec3(0.0); // essentially an rgb specular color extracted from the albedo through metalness
|
||||
float metalness = 0.0; // dielectric or metallic surface
|
||||
float cavity = 0.5; // hard multiplier
|
||||
float ao = 1.0; // detail occluder for lights
|
||||
|
||||
// results
|
||||
vec3 color = vec3(0.0);
|
||||
vec3 lights = vec3(0.0);
|
||||
vec3 citylights = vec3(0.0);
|
||||
|
||||
// because we have two uvs, for poles and equator, to avoid polar distortions and hide tiling, we need to run most things twice
|
||||
mat3 TBNA = mat3(0.0);
|
||||
mat3 TBNB = mat3(0.0);
|
||||
|
||||
// build splatting basis
|
||||
vec3 detailSampA = vec3(texture2D(biomes, uvS.xy + biomesPicks[0].xy).a, texture2D(biomes, uvS.xy + biomesPicks[1].xy).a, texture2D(biomes, uvS.xy + biomesPicks[2].xy).a);
|
||||
vec3 detailSampB = vec3(texture2D(biomes, uvS.zw + biomesPicks[0].xy).a, texture2D(biomes, uvS.zw + biomesPicks[1].xy).a, texture2D(biomes, uvS.zw + biomesPicks[2].xy).a);
|
||||
|
||||
vec3 splatMaskMixedA = vec3(detailSampA.r + detailSampA.g, detailSampA.g + detailSampA.b, detailSampA.b); // last one comes later
|
||||
vec3 splatMaskMixedB = vec3(detailSampB.r + detailSampB.g, detailSampB.g + detailSampB.b, detailSampB.b); // last one comes later
|
||||
|
||||
splatMaskMixedA = min(vec3(1.0), (splatMaskMixedA.rgb + splatMap.rgb) * splatMap.rgb);
|
||||
splatMaskMixedB = min(vec3(1.0), (splatMaskMixedB.rgb + splatMap.rgb) * splatMap.rgb);
|
||||
|
||||
// optional
|
||||
splatMaskMixedA = pow(splatMaskMixedA.rgb, vec3(splatSharpness));
|
||||
splatMaskMixedB = pow(splatMaskMixedB.rgb, vec3(splatSharpness));
|
||||
|
||||
// create combined detail texture and calculate weighted splat mapping for the two uv sets
|
||||
detailSampA.r = mix(mix(detailSampA.r, detailSampA.g, splatMaskMixedA.r), detailSampA.b, splatMaskMixedA.g) - cracksWaterMask.y;
|
||||
detailSampB.r = mix(mix(detailSampB.r, detailSampB.g, splatMaskMixedB.r), detailSampB.b, splatMaskMixedB.g) - cracksWaterMask.y;
|
||||
|
||||
if (normalMapping){
|
||||
|
||||
vec3 dp1 = dp1Calc(-v);
|
||||
vec3 dp2 = dp2Calc(-v);
|
||||
// derive for both uv's
|
||||
vec4 duv1 = duv1Calc(uvS);
|
||||
vec4 duv2 = duv2Calc(uvS);
|
||||
|
||||
vec3 dp2perp = cross(dp2, normal);
|
||||
vec3 dp1perp = cross(normal, dp1);
|
||||
|
||||
// create matrix A
|
||||
vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 binormal = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
float invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBNA = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
// create matrix B
|
||||
tangent = dp2perp * duv1.z + dp1perp * duv2.z;
|
||||
binormal = dp2perp * duv1.w + dp1perp * duv2.w;
|
||||
invmax = pow(max(dot(tangent, tangent), dot(binormal, binormal)), -0.5);
|
||||
TBNB = mat3(tangent * invmax, binormal * invmax, normal);
|
||||
|
||||
if (parallax){
|
||||
|
||||
// water depth refraction
|
||||
float depthRefraction = mix(1.33,0.66, square(NdotV.r)); // water refraction index is 1.33, square so transparency wont cancel it out later
|
||||
detailSampA.r *= mix(depthRefraction, 1.0, ceil(1.0 - detailSampA.r));
|
||||
detailSampB.r *= mix(depthRefraction, 1.0, ceil(1.0 - detailSampB.r));
|
||||
|
||||
// do parallax
|
||||
float fDet = dot(dp1, dp2perp);
|
||||
vec2 vProjVScr = (1.0/fDet) * vec2(dot(dp2perp, v), dot(dp1perp, v));
|
||||
vec4 vProjVTex = (duv1 * vProjVScr.x + duv2 * vProjVScr.y);
|
||||
vec2 vProjVTexZ = NdotV.r * (vec2(detailSampA.r, detailSampB.r) * scaleBias.r - scaleBias.g);
|
||||
|
||||
uvS += vProjVTex * vProjVTexZ.xxyy;
|
||||
}
|
||||
}
|
||||
// sample all four biomes once for each of the first two uv sets
|
||||
vec4 firstbiomeA = texture2D(biomes, uvS.xy + biomesPicks[0].xy);//red
|
||||
vec4 firstBiomeB = texture2D(biomes, uvS.zw + biomesPicks[0].xy);//red
|
||||
vec4 secondbiomeA = texture2D(biomes, uvS.xy + biomesPicks[1].xy);//green
|
||||
vec4 secondBiomeB = texture2D(biomes, uvS.zw + biomesPicks[1].xy);//green
|
||||
vec4 thirdbiomeA = texture2D(biomes, uvS.xy + biomesPicks[2].xy);//black
|
||||
vec4 thirdBiomeB = texture2D(biomes, uvS.zw + biomesPicks[2].xy);//black
|
||||
|
||||
// mix the normals cavity and resampled heightmap
|
||||
vec4 dataSampA = mix(mix(firstbiomeA, secondbiomeA, splatMaskMixedA.r), thirdbiomeA, splatMaskMixedA.g);
|
||||
vec4 dataSampB = mix(mix(firstBiomeB, secondBiomeB, splatMaskMixedB.r), thirdBiomeB, splatMaskMixedB.g);
|
||||
|
||||
// create combined detail splatmask to mix the first two uv sets
|
||||
float uvBlend = min(1.0, (dataSampA.a + dataSampB.a + vertCol.r) * vertCol.r);
|
||||
|
||||
// mix the height for accurate combined watermask and add cavity just because we can
|
||||
vec4 dataSamp = vec4(mix(dataSampA.ba, dataSampB.ba, uvBlend), 0.0, 0.0);
|
||||
dataSamp.a = dataSamp.g - cracksWaterMask.y;
|
||||
dataSamp.z = max(0.0, ceil(dataSamp.a));
|
||||
cracksWaterMask.y = 1.0 - cracksWaterMask.y;
|
||||
dataSamp.x = mix(0.5, dataSamp.x, dataSamp.z);
|
||||
ao = min(1.0,(dataSamp.x * 2.0));
|
||||
|
||||
// yeah the edges will make horrible distortions but we lerp them out and save a sample
|
||||
float waterLavaMix = (mix(8.0, (1.0 - ao * 0.01 + pulse * 0.01) * 2.0, dataSamp.z));
|
||||
vec4 textureNoise = mix(texture2D(differenceNoise, uvNoise.xy * waterLavaMix), texture2D(differenceNoise, uvNoise.zw * waterLavaMix), vertCol.r);
|
||||
vec2 cracksWaterNorms = vec2(mix(vec2(textureNoise.r, textureNoise.g), vec2(textureNoise.g, textureNoise.b), pulse)) - 0.5;// // could maybe be replaced by a small simple 2D noise function
|
||||
|
||||
float cracks = min(1.0, square(1.0 - ao) * cracksWaterMask.r);
|
||||
// cracksWaterNorms is just noise, so it can work for cracks intensity here
|
||||
lights = pow3(paletteCracks(fract(cracks * 0.025 + cracksColorIntensity.x)) * cracks * cracksColorIntensity.y);
|
||||
|
||||
float underWaterBlur = pow((1.0 - dataSamp.z) * (1.0 - dataSamp.a - 1.0), 0.5) * 7.0 + 1.0;
|
||||
vec4 cityA = texture2D(cities, uvS.xy * vec2(4.0, 2.0), underWaterBlur);
|
||||
vec4 cityB = texture2D(cities, uvS.zw * vec2(4.0, 2.0), underWaterBlur);
|
||||
vec3 lightsA = texture2D(cityGlow, uvS.xy * vec2(4.0, 2.0), underWaterBlur).rgb;
|
||||
vec3 lightsB = texture2D(cityGlow, uvS.zw * vec2(4.0, 2.0), underWaterBlur).rgb;
|
||||
|
||||
vec2 citySplat = clamp(vec2(pow5((1.0 - abs(vec2(dataSampA.a, dataSampB.a) - 0.5) * 2.0)) * (abs(vec2(cityA.a, cityB.a) - 0.5) + vec2(cityA.a, cityB.a))) * splatMap.a, vec2(0.0), vec2(1.0));
|
||||
|
||||
if (normalMapping){
|
||||
// perform TBN matrix multiplication for each of the two normal maps separately and mix, save instructions by mix in height in alpha.
|
||||
n = vec4(mix(dataSampA.xy, cityA.xy, citySplat.r), mix(dataSampB.xy, cityB.xy, citySplat.g));
|
||||
n *= 2.0;
|
||||
n -=1.0;
|
||||
//create waterNormalMap
|
||||
n = mix(vec4(cracksWaterNorms, cracksWaterNorms) * 0.1, n, dataSamp.z - (1.0 - square(ao)) * cracksWaterMask.x);
|
||||
|
||||
vec3 nA = vec3(n.xy, deriveZ(n.xy));
|
||||
vec3 nB = vec3(n.zw, deriveZ(n.zw));
|
||||
n = mix(vec4(normalize(TBNA * nA), dataSampA.a), vec4(normalize(TBNB * nB), dataSampB.a), uvBlend);
|
||||
r = normalize(reflect(-v, n.xyz));
|
||||
|
||||
}
|
||||
|
||||
vec3 cityData = mix(vec3(cityA.ba, citySplat.r), vec3(cityB.ba, citySplat.g), uvBlend);
|
||||
// create combined lookup gradients for all four biomes, clamped to not wrap the texture.
|
||||
vec3 albedoGradients = mix(vec3(firstbiomeA.a, secondbiomeA.a, thirdbiomeA.a), vec3(firstBiomeB.a, secondBiomeB.a, thirdBiomeB.a), uvBlend);
|
||||
// and make the flat slopes have less color shifts - think silt vs cliff sides.
|
||||
albedoGradients = clamp((1 - albedoGradients) + (vec3(biomesPicks[0].w,biomesPicks[1].w,biomesPicks[2].w) * vertCol.g), vec3(0.00390625), vec3(0.984375));
|
||||
|
||||
// sample albedo and roughness from lookup table
|
||||
vec4 firstBiomesAlbedoR = texture2D(lookup, vec2(biomesPicks[0].z, albedoGradients.r)); //, 0.0
|
||||
vec4 secondBiomesAlbedoR = texture2D(lookup, vec2(biomesPicks[1].z, albedoGradients.g)); //, 0.0
|
||||
vec4 thirdBiomesAlbedoR = texture2D(lookup, vec2(biomesPicks[2].z, albedoGradients.b)); //, 0.0
|
||||
|
||||
// create final splatmap , save instructions by mix in cavity in alpha.
|
||||
vec4 albedoSplatCavity = mix(vec4(splatMaskMixedA, dataSampA.b), vec4(splatMaskMixedB, dataSampB.b), uvBlend);
|
||||
|
||||
// mix final albedo and tweak with cavity roughness values
|
||||
albedoR = mix(mix(firstBiomesAlbedoR, secondBiomesAlbedoR, albedoSplatCavity.r), thirdBiomesAlbedoR, albedoSplatCavity.g);
|
||||
|
||||
cityData.rg = cityData.rg * 0.33 + 0.5;
|
||||
albedoR = mix(albedoR, vec4(playerPlateProfile.rgb * cityData.g, playerPlateProfile.a) * cityData.r, cityData.b);
|
||||
|
||||
NdotV.g = (1.0 - NdotV.r) * 0.5 + 0.5;
|
||||
NdotV.r = dot(n.xyz, v);
|
||||
NdotV = max(vec2(0.0), NdotV);
|
||||
|
||||
// creates ocean color
|
||||
vec3 oceanColor = texture2D(lookup, vec2(biomesPicks[0].z, max(0.995, cracksWaterMask.y * 0.05))).rgb; // from 0.0065 to 0.0025
|
||||
|
||||
//creates lights
|
||||
citylights += mix(lightsA, lightsB, uvBlend);
|
||||
citylights.rg *= (1.5 - underWaterBlur * 0.1) * min(1.0, cityData.b + (1.0 - dataSamp.z) * 0.5 * splatMap.a);
|
||||
citylights.g *= 0.5;
|
||||
citylights.b *= splatMap.a * 0.5;
|
||||
citylights = clamp(vec3(population) - vec3(0.0, 15.0, 30.0), vec3(0.0), vec3(1.0)) * citylights * emissiveIntensity;
|
||||
citylights = toLinear(citylights.r * colorLightsPrimary) + toLinear(citylights.g * colorLightsSecondary) + toLinear(citylights.b * colorLightsTertiary);
|
||||
citylights *= min(vec3(1.0), square(oceanColor.rgb) + dataSamp.z);
|
||||
//
|
||||
// creates oceans
|
||||
albedoR = mix(vec4((oceanColor / (1.0 + (1.0 - NdotV.y) * 0.75)) * mix(min(1.0, dataSamp.y * dataSamp.x * pow3(cracksWaterMask.y) * 16.0 + 0.25), 1.0, NdotV.y), 0.25), albedoR, dataSamp.z);
|
||||
|
||||
// clamp to keep in PBR safe ranges - nothing in reality either albedo nor roughness is 0 or 1 and can make the math fail
|
||||
albedoR = clamp(albedoR, vec4(0.05), vec4(0.975));
|
||||
|
||||
|
||||
// create sss mask
|
||||
vec4 invertedAlbedo = 1.0 - albedoR;
|
||||
float SSSmask = mix(NdotV.r * 0.5 + 0.5, 1.0 - min(1.0, pow8((1.0 - invertedAlbedo.a * invertedAlbedo.b) * (1.0 - invertedAlbedo.r * invertedAlbedo.g))) * dataSamp.y * dataSamp.x, dataSamp.z);
|
||||
|
||||
// creates a metalness value if biomes are above 1950 on the lookup, for metallic specular on crystals.
|
||||
metalness = square(n.a) * ceil(mix(mix(biomesPicks[0].z, biomesPicks[1].z, albedoSplatCavity.r), biomesPicks[2].z, albedoSplatCavity.g) - 0.9521484375) * dataSamp.z;
|
||||
|
||||
albedoR.rgb = toLinear(albedoR.rgb);
|
||||
substance = (0.04 - 0.04 * metalness) + albedoR.rgb * metalness;
|
||||
albedoR.rgb -= substance;
|
||||
|
||||
|
||||
// actual shading starts here
|
||||
vec3 ambientFresnel = Fresnel2(substance, NdotV.r ,albedoR.a);
|
||||
|
||||
if (advancedAmbience){
|
||||
|
||||
color += square((textureCube(skybox, r, sqrt(albedoR.a) * 8.0).rgb) + 0.024) * ambientFresnel;
|
||||
|
||||
// ambient light
|
||||
color += square(textureCube(skybox, n.xyz, 8.0).rgb + 0.024) * albedoR.rgb * (1.0 - ambientFresnel);
|
||||
}
|
||||
else if (simpleAmbience){
|
||||
// Ambient reflections with fix mip and n instead of refect
|
||||
color += square(textureCube(skybox, r, 3.0).rgb * ambientFresnel);
|
||||
|
||||
// Ambient light - average color of skybox squared
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * albedoR.rgb * (1.0 - ambientFresnel);
|
||||
}
|
||||
else{
|
||||
// Ambient
|
||||
color += vec3(0.006724, 0.014884, 0.067081) * (ambientFresnel + albedoR.rgb * (1.0 - ambientFresnel));
|
||||
}
|
||||
|
||||
if (pbrLight){
|
||||
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
vec2 NdotL = max(vec2(0.0), vec2(dot(mix(n.xyz, normalize(normal), 0.5),L) * 0.8 + 0.2, dot(n.xyz,L)));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L) )));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL.x;
|
||||
|
||||
citylights *= clamp(1.0 - attenuation, 0.0, 1.0);
|
||||
|
||||
if (attenuation >0.0){
|
||||
vec3 VplusL = L + v * 0.5; // *0.5 is not correct but without it grazing angle specular fails, propably somewhere something is flipped space/normal space
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n.xyz));
|
||||
vec3 F = Fresnel(substance, L, halfVec);
|
||||
float D = max(0.0, D_GGX(HdotN, albedoR.a));
|
||||
float V = max(0.0, V_SchlickforGGX((1.0 + albedoR.a) * 0.5, NdotV.r, NdotL.y));
|
||||
float O = OrenNayerforDiffuseOnly(albedoR.a, NdotL.y, NdotV.r);
|
||||
|
||||
vec3 A = vec3(1.0);
|
||||
vec3 SSS = vec3(0.0);
|
||||
if (scattering) {
|
||||
// atmospheric light tinting simulating scattering
|
||||
A = vec3(paletteAtmoTint(1.0 - max(0.0, min(0.33, dot(normal,L) + 0.15))));
|
||||
|
||||
// sub surface scattering model
|
||||
float inScatter = pow(clamp(dot(L, -v), 0.0, 1.0), 12.0) * mix(8.0, 0.1, SSSmask);
|
||||
float normalContribution = clamp(dot(mix(normal, n.xyz, SSSmask), halfVec) * SSSmask + 1.0 - SSSmask, 0.0, 1.0);
|
||||
float backScatter = dataSamp.x * normalContribution / tau;
|
||||
SSS = mix(backScatter, 1.0, inScatter) * square(oceanColor) * NdotL.x;
|
||||
}
|
||||
color += ((D * V * F) + ((1.0 - F) * O * albedoR.rgb) + SSS) * gl_LightSource[i].diffuse.rgb * A * attenuation;
|
||||
}
|
||||
}
|
||||
// hard cavity multiplier
|
||||
color *= dataSamp.x - metalness + 1.0;
|
||||
}
|
||||
// probably not worth it to check NdotL like pbr
|
||||
else{
|
||||
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
float distance = length(gl_LightSource[i].position.xyz - pos.xyz); // EYE SPACE, I'm sorry:o(
|
||||
// rest is world space
|
||||
|
||||
vec3 L = normalize(light[i] / distance);
|
||||
float NdotL = max(0.0, dot(n.xyz,L));
|
||||
|
||||
float sqrLightRadius = square(lightRadius[i]);
|
||||
float illuminance = lightIntensity * pi * (sqrLightRadius / (max(sqrLightRadius, dot(L,L))));
|
||||
|
||||
// note the square to kill hard spec in deep space!
|
||||
float attenuation = square(1.0 / (1.0 + (gl_LightSource[i].constantAttenuation
|
||||
+ gl_LightSource[i].linearAttenuation * distance
|
||||
+ gl_LightSource[i].quadraticAttenuation * square(distance)))) * illuminance * NdotL;
|
||||
|
||||
citylights *= clamp(1.0 - attenuation, 0.0, 1.0);
|
||||
vec3 VplusL = L + v;
|
||||
vec3 halfVec = normalize(VplusL);
|
||||
float HdotN = max(0.0, dot(halfVec, n.xyz));
|
||||
vec3 S = Fresnel2(substance, HdotN ,albedoR.a);
|
||||
// albedoR.rgb * (dataSamp.x * 0.75) to sorta hack albedo into a classical diffuse texture
|
||||
lights += max(vec3(0.001), albedoR.rgb * (dataSamp.x * 0.75) + pow(S * HdotN, vec3(albedoR.a + 5.0))) * gl_LightSource[i].diffuse.rgb * attenuation;
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor.rgb = toGamma(clamp(color + lights + citylights, vec3(0.0), vec3(1.0)));
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_uv;
|
||||
attribute vec4 in_color;
|
||||
attribute vec4 in_uv2;
|
||||
|
||||
uniform float time;
|
||||
uniform vec4 wsRot;
|
||||
uniform vec4 wsPos;
|
||||
uniform vec2 texSize;
|
||||
|
||||
varying vec4 uvNoise, pos;
|
||||
varying vec3 normal, npos, vertCol;
|
||||
varying vec2 uv, uv2, uv3, uvB[5];
|
||||
varying float pulse;
|
||||
varying vec3 light[nLightCount];
|
||||
varying vec3 lightColor[nLightCount];
|
||||
varying float dist[nLightCount];
|
||||
|
||||
const vec2 fractions = vec2(0.984375, 0.0078125);
|
||||
/*
|
||||
vec2 rotator(vec2 rotate, float rate)
|
||||
{
|
||||
return vec2(dot(rotate, vec2(cos(rate), -sin(rate))), dot(rotate, vec2(sin(rate), cos(rate))));
|
||||
}/*
|
||||
// regular atan() fails at 0,0
|
||||
float atan2(float y, float x)
|
||||
{
|
||||
return x == 0.0 ? sign(y)*pi/2 : atan(y, x);
|
||||
}*/
|
||||
vec3 toLinear(vec3 x) {
|
||||
return pow(x, vec3(2.2));
|
||||
}
|
||||
|
||||
vec3 wsAllign(vec3 x){
|
||||
return x + 2.0 * cross(wsRot.xyz, cross(wsRot.xyz, x) + wsRot.w * x);
|
||||
}
|
||||
|
||||
vec2 invertBlurEdge(vec2 uvB){
|
||||
return abs(fract((uvB + 1.0) * 0.5) - 0.5) * 2.0;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
pulse = abs(time * 2.0 - 1.0);
|
||||
pos = gl_ModelViewMatrix * in_vertex;
|
||||
|
||||
// convert view, normal and light vectors to world space and quaternion correct for model rotation
|
||||
mat3 tcamrot = transpose(mat3x3(gl_ModelViewMatrix));
|
||||
npos = (wsAllign(normalize(tcamrot * -pos.xyz)));
|
||||
|
||||
// special view vector to correct just for cubemap reflections
|
||||
normal = (tcamrot * (gl_NormalMatrix * wsAllign(normalize(in_normal))));
|
||||
for (int i = 0; i < nLightCount; i++) {
|
||||
light[i] = wsAllign(normalize((tcamrot * (((gl_LightSource[i].position)).xyz - pos.xyz))));
|
||||
}
|
||||
|
||||
uv = in_uv;
|
||||
uv2 = in_uv2.xy;
|
||||
uv3 = in_uv2.zw;
|
||||
uv3.x = 1.0 - uv3.x;
|
||||
|
||||
// make splatmap blurs
|
||||
vec2 blur = vec2(1.0 / texSize.x, 1.0 / texSize.y);
|
||||
uvB[0] = uv3;
|
||||
uvB[1] = uv3 + blur;
|
||||
uvB[2] = uv3 + vec2(-blur.x, blur.y);
|
||||
uvB[3] = uv3 + vec2(-blur.x, -blur.y);
|
||||
uvB[4] = uv3 + vec2(blur.x, -blur.y);
|
||||
|
||||
// clamp so we don't get south poles blending into north poles an vice versa
|
||||
blur.y = 1.0 - blur.x;
|
||||
uvB[0].y = clamp(uvB[0].y, blur.x, blur.y);
|
||||
uvB[1].y = clamp(uvB[1].y, blur.x, blur.y);
|
||||
uvB[2].y = clamp(uvB[2].y, blur.x, blur.y);
|
||||
uvB[3].y = clamp(uvB[3].y, blur.x, blur.y);
|
||||
uvB[4].y = clamp(uvB[4].y, blur.x, blur.y);
|
||||
|
||||
uvNoise = ((vec4(uv, uv2) * 3.0) * fractions.x + fractions.y) / 3.0;
|
||||
|
||||
vertCol = in_color.rgb;
|
||||
// use to kill off the worst polar splatmap distortions
|
||||
vertCol.b *= 0.6;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
const int nLightCount = 2;
|
||||
|
||||
uniform sampler2D diffuseRGBspecA;
|
||||
uniform sampler2D cities;
|
||||
uniform float population;
|
||||
|
||||
//Amount of population considered 'half full'
|
||||
const float basePopulation = 8.0;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
vec3 nightLights(vec3 incidentLight) {
|
||||
float brightness = clamp(max(incidentLight.r, max(incidentLight.g, incidentLight.b)) * 0.9, 0.0, 1.0);
|
||||
if(brightness > 0.4)
|
||||
return vec3( 0.0 );
|
||||
brightness /= 0.4;
|
||||
|
||||
float popDensity = population / basePopulation;
|
||||
float level = texture2D(cities, uv).r;
|
||||
|
||||
return vec3( level * clamp(level - 1.0 + popDensity, 0.0, 1.0) * clamp(level - brightness, 0.0, 1.0) );
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 texSamp = texture2D(diffuseRGBspecA, uv);
|
||||
vec3 matspec = gl_FrontMaterial.specular.rgb * texSamp.a;
|
||||
float shininess = gl_FrontMaterial.shininess * (0.5 + texSamp.a);
|
||||
|
||||
vec3 n = normalize(normal);
|
||||
vec3 v = normalize(npos);
|
||||
|
||||
vec3 diffuse = vec3(0);
|
||||
vec3 specular = vec3(0);
|
||||
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
float intensity = max(0.0, dot(n, light[i]));
|
||||
if(intensity > 0.0) {
|
||||
//Apply falloff
|
||||
intensity /= (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
vec3 r = -reflect(light[i], n);
|
||||
specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
|
||||
}
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
float intensity = max(0.0, dot(n, light[i]));
|
||||
if(intensity > 0.0) {
|
||||
//Apply falloff
|
||||
intensity /= (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
|
||||
vec3 r = -reflect(light[i], n);
|
||||
specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity);
|
||||
}
|
||||
}
|
||||
diffuse *= gl_FrontMaterial.diffuse.rgb;
|
||||
specular *= matspec;
|
||||
|
||||
vec3 ambient = gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;
|
||||
|
||||
gl_FragColor.rgb = ((diffuse + ambient) * texSamp.rgb) + specular + nightLights(diffuse);
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#version 120
|
||||
const int nLightCount = 2;
|
||||
|
||||
const float noiseSplit = 0.6;
|
||||
|
||||
uniform sampler2D texture, hardNoise;
|
||||
uniform float plSize, ringMin, ringMax;
|
||||
uniform vec2 starDir;
|
||||
|
||||
varying vec3 normal, binormal, tangent;
|
||||
varying vec3 npos;
|
||||
varying vec2 uv;
|
||||
|
||||
vec3 light[nLightCount];
|
||||
float dist[nLightCount];
|
||||
|
||||
void main() {
|
||||
float radius = (length(uv) - 1.0) / 1.48;
|
||||
vec3 color = gl_FrontMaterial.diffuse.rgb;
|
||||
vec4 samp = texture2D(texture, vec2(0.0,radius));
|
||||
|
||||
vec3 norm = vec3(0.0);
|
||||
norm += texture2D(hardNoise, uv).xyz;
|
||||
norm += texture2D(hardNoise, uv * 1.5 + vec2(0.15)).xyz;
|
||||
norm += texture2D(hardNoise, uv * 2.0 + vec2(0.25)).xyz;
|
||||
norm += texture2D(hardNoise, uv * 0.5 + vec2(0.41)).xyz;
|
||||
norm = mod(norm, 1.0);
|
||||
|
||||
vec3 noiseSamp = texture2D(hardNoise, uv * 4.0).rgb;
|
||||
|
||||
samp.a *= noiseSamp.b;
|
||||
samp.rgb *= (1.0 - noiseSplit + (noiseSamp.g * 2.0 * noiseSplit));
|
||||
|
||||
samp.a *= smoothstep(ringMin - 0.05, ringMin, radius);
|
||||
samp.a *= 1.0 - smoothstep(ringMax, ringMax + 0.05, radius);
|
||||
if(samp.a < 0.01)
|
||||
discard;
|
||||
|
||||
const float gloss = 1.0;
|
||||
float shininess = 100.0;//gl_FrontMaterial.shininess * gloss;
|
||||
|
||||
vec3 n = normalize(gl_NormalMatrix * (norm.xyz - vec3(0.5)));
|
||||
vec3 v = normalize(npos);
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
light[i] = gl_LightSource[i].position.xyz + npos;
|
||||
dist[i] = length(light[i]);
|
||||
light[i] = light[i] / dist[i];
|
||||
}
|
||||
|
||||
vec3 ambient = gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb;
|
||||
vec3 diffuse = vec3(0.0);
|
||||
float normContrib = (100.0 / (length(npos) + 100.0));
|
||||
normContrib *= pow(abs(dot(normalize(normal),normalize(npos))), 0.5);
|
||||
|
||||
if(nLightCount > 0) {
|
||||
const int i = 0;
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float intensity = mix(max(0.0, dot(n, light[i])), 1.0, 1.0 - normContrib) * falloff;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
}
|
||||
if(nLightCount > 1) {
|
||||
const int i = 1;
|
||||
float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i]));
|
||||
float intensity = mix(max(0.0, dot(n, light[i])), 1.0, 1.0 - normContrib) * falloff;
|
||||
|
||||
diffuse += gl_LightSource[i].diffuse.rgb * intensity;
|
||||
}
|
||||
|
||||
//float penumbra = 1.0 - tan(40.0/length(starDir));
|
||||
float shadow = 0.0;
|
||||
float shadowDot = -dot(normalize(uv), normalize(starDir));
|
||||
|
||||
if(shadowDot > 0.0) {
|
||||
//shadow += clamp((shadowDot - penumbra) * 30.0, 0.0, 1.0);
|
||||
float umbraDist = abs(dot(uv, normalize(starDir.yx * vec2(-1.0,1.0))));
|
||||
float umbra = (plSize - umbraDist) * 40.0;
|
||||
float penumbra = min(umbra, 0.0) + length(uv) * 8000.0 / length(starDir);
|
||||
shadow += umbra + penumbra;
|
||||
}
|
||||
|
||||
shadow = clamp(1.0 - shadow, 0.0, 1.0);
|
||||
|
||||
//float penumbra = 1.0 - tan(40.0/length(starDir));
|
||||
|
||||
//float shadow = 1.0 - clamp((-dot(normalize(uv), normalize(starDir)) - penumbra) * 30.0, 0.0, 1.0);
|
||||
|
||||
samp.rgb *= (1.0 - normContrib * 0.5);
|
||||
|
||||
vec4 reservedKeyword = vec4(0.0, 0.0, 0.0, samp.a);
|
||||
reservedKeyword.rgb = samp.rgb * (diffuse * shadow + ambient);
|
||||
reservedKeyword.a *= smoothstep(0, 0.2, radius);
|
||||
reservedKeyword.a *= 1.0 - smoothstep(0.95, 1.0, radius);
|
||||
|
||||
gl_FragColor = reservedKeyword;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
attribute vec4 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec4 in_tangent;
|
||||
|
||||
varying vec3 npos;
|
||||
varying vec3 normal, binormal, tangent;
|
||||
varying vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
normal = normalize(gl_NormalMatrix * in_normal);
|
||||
tangent = normalize(gl_NormalMatrix * in_tangent.xyz);
|
||||
binormal = normalize(gl_NormalMatrix * cross(normal, in_tangent.xyz * in_tangent.w));
|
||||
|
||||
vec4 localVert = in_vertex;
|
||||
localVert.y *= 0.2;
|
||||
|
||||
vec4 pos = gl_ModelViewMatrix * localVert;
|
||||
npos = -pos.xyz;
|
||||
|
||||
uv = in_vertex.xz;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user