Skip to content

Commit

Permalink
Implement Terrain Blending
Browse files Browse the repository at this point in the history
  • Loading branch information
sturnclaw committed Sep 24, 2020
1 parent b0ab9cf commit 28a559e
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 29 deletions.
102 changes: 77 additions & 25 deletions data/shaders/opengl/geosphere_terrain.frag
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ in vec4 varyingEmission;
out vec4 frag_color;

// Calculate Blinn-Phong shading for a light's contribution to the fragment
vec4 CalcLightContribution(int i, vec3 V, vec3 N, vec3 geospherePos, inout vec3 specular)
vec4 CalcLightContribution(int i, vec3 V, vec3 N, vec3 geospherePos, inout vec4 specular)
{
// All lights processed by this shader are directional lights, assumed to be
// infinitely far away from the surface of the geosphere. As such, the light
Expand Down Expand Up @@ -73,13 +73,80 @@ vec4 CalcLightContribution(int i, vec3 V, vec3 N, vec3 geospherePos, inout vec3

//Specular reflection
vec3 H = normalize(L + V);
specular += pow(max(dot(N, H), 0.0), 64.0) * uLight[i].specular.xyz * INV_NUM_LIGHTS;
specular += pow(max(dot(N, H), 0.0), 64.0) * uLight[i].specular * INV_NUM_LIGHTS;

// TODO: PBR lighting model

return uLight[i].diffuse * uneclipsed * lighting;
}

void ReadTerrainTextureMix(vec2 uv, float layer, out vec4 textureData)
{
#if 1
// cheap way of limiting the visual appearance of texture tiling
float scaleFactor = exp(-0.0002 * dist);
vec4 s1 = texture(texture3, vec3(uv * detailScaleLo * -0.25, layer)).rgba;
vec4 s2 = texture(texture3, vec3(uv * detailScaleLo, layer)).rgba;
textureData = mix(s1, s2, 0.5);
#else
textureData = texture(texture3, vec3(uv, layer)).rgba;
#endif
}

void SampleTerrainTexture(out vec4 diffuseColor)
{
#ifdef TEXTURE0
// LookupTexture & slope/height texture coords

// size of the blending tap in fractions of a pixel
#define TAP_EPSILON 0.45
#define ONE_MINUS_TAP_EPSILON (1.0 - TAP_EPSILON)
vec2 texSize = textureSize(texture2, 0).rg;

// get the location of the current UV coordinate inside the sampled texel
vec2 texelCoord = mod(uv1 * texSize, 1.0);
// get the offset of the coordinate relative to the texel center and make offsets
vec2 tapOffset = sign(texelCoord - vec2(0.5)) * vec2(TAP_EPSILON) / texSize;
// and generate the UVs for the two texture taps.
vec2 tap1Uv = uv1 + vec2(tapOffset.x, 0.0);
vec2 tap2Uv = uv1 + vec2(0.0, tapOffset.y);

// Do a simple multi-tap sampling to blend textures at borders
float terrainType = texture(texture2, uv1).r;
float terrainTypeTap1 = texture(texture2, tap1Uv).r;
float terrainTypeTap2 = texture(texture2, tap2Uv).r;

// We tap the terrain type lookup three times, but only perform two texture samples against the actual texture
vec2 absOffset = abs(texelCoord - vec2(0.5)) * 2.0;
float terrainType2 = 0.0;
float blendSrc = 0.0;

// Select the highest-weighted terrain type
if (absOffset.x > absOffset.y) {
terrainType2 = terrainTypeTap1;
blendSrc = absOffset.x;
} else {
terrainType2 = terrainTypeTap2;
blendSrc = absOffset.y;
}
float blendWeight = max((blendSrc - ONE_MINUS_TAP_EPSILON) * (1.0 / TAP_EPSILON), 0.0);

// use the size of the terrain texture array to scale the texture type value
// FIXME: define this in a uniform and hook it into terrain atlas definition
float size = textureSize(texture3, 0).z;

vec4 terrainColor1;
vec4 terrainColor2;
ReadTerrainTextureMix(uv0, terrainType * size, terrainColor1);
ReadTerrainTextureMix(uv0, terrainType2 * size, terrainColor2);

// lerp between the two textures (blendweight is divided by 2 because the blend is split across two LUT texels)
diffuseColor = mix(terrainColor1, terrainColor2, blendWeight * 0.5);
#else
diffuseColor = vec4(0.313, 0.313, 0.313, 1.0);
#endif
}

void main(void)
{
vec4 hidetail = texture(texture0, uv0 * detailScaleHi);
Expand All @@ -95,35 +162,20 @@ void main(void)
vec4 detailVal = mix(lodetail, hidetail, hiloMix);
vec4 detailMul = mix(vec4(1.0), detailVal, detailMix);

vec3 specular = vec3(0.0);
vec4 color = vec4(0.0);
vec4 specular = vec4(0.0);

vec4 specularIntensity = vec4(0.0); // todo: sample this from a texture.

#ifdef TEXTURE0
// LookupTexture & slope/height texture coords
float terrainType = texture(texture2, uv1).r;
// mul by 16 because values are 0..1 but we need them 0..15 for array indexing
int id0 = int(terrainType * 16.0);

#if 1
// cheap way of limiting the visual appearance of texture tiling
// NB: this darkens the texture input
vec4 s1 = texture(texture3, vec3(uv0 * detailScaleLo, float(id0))).rgba;
vec4 s2 = texture(texture3, vec3(uv0 * detailScaleLo * -0.25, float(id0))).rgba;
vec4 color = mix(s1, s2, 0.5);
#else
vec4 color = texture(texture3, vec3(uv0 * detailScaleLo, float(id0))).rgba;
#endif
#else
vec4 color = vec4(0.313, 0.313, 0.313, 1.0);
#endif
SampleTerrainTexture(color);

vec3 geospherePos = (fragPos - geosphereCenter) * geosphereInvRadius;
vec3 V = -normalize(fragPos);
vec4 specularAccum = vec4(0.0);
for (int i=0; i<NUM_LIGHTS; ++i) {
diff += CalcLightContribution(i, V, normal, geospherePos, specular);
diff += CalcLightContribution(i, V, normal, geospherePos, specularAccum);
}

specular *= specularAccum;

// Use the detail value to multiply the final colour before lighting
vec4 final = color * detailMul;

Expand All @@ -150,7 +202,7 @@ void main(void)
fogFactor *
((scene.ambient * final) + (diff * final)) + // diffuse term
(1.0 - fogFactor) * (diff * atmosColor) +
(vec4(specular, 1.0) * specularIntensity * sunset) +
(specular * sunset) +
(0.02 - clamp(fogFactor, 0.0, 0.01)) * diff * ldprod * sunset + //increase fog scatter
(pow(1.0 - pow(fogFactor,0.75), 256.0) * 0.4 * diff * atmosColor) * sunset; //distant fog.
#else // atmosphere-less planetoids and dim stars
Expand Down
2 changes: 1 addition & 1 deletion data/terrain/EarthLike.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name" : "template",
"LUT" : "terrainLUT",
"LUT" : "terrainLUT.png",
"Layers" : 16,
"detailTextureHigh" : "high.dds",
"detailTextureLow" : "low.dds",
Expand Down
2 changes: 1 addition & 1 deletion data/terrain/EarthLikeHeightmapped.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name" : "template",
"LUT" : "terrainLUT",
"LUT" : "terrainLUTrandom.png",
"Layers" : 16,
"detailTextureHigh" : "high.dds",
"detailTextureLow" : "low.dds",
Expand Down
3 changes: 1 addition & 2 deletions src/GeoSphere.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt

#include "FileSystem.h"
#include "GeoSphere.h"
#include "FileSystem.h"

Expand Down Expand Up @@ -523,7 +522,7 @@ void GeoSphere::LoadTerrainJSON(const std::string &path)
// load the look-up-table
Json lut = rootNode["LUT"];
assert(!lut.empty() && lut.is_string());
m_surfaceLUT.Reset(Graphics::TextureBuilder::LookUpTable(lut).GetOrCreateTexture(Pi::renderer, "lut"));
m_surfaceLUT.Reset(Graphics::TextureBuilder::LookUpTable(stringf("textures/terrain/%0", lut.get<std::string>())).GetOrCreateTexture(Pi::renderer, "lut"));

// what detail textures does this terrain use
const std::string detailTextureHigh = rootNode["detailTextureHigh"];
Expand Down

0 comments on commit 28a559e

Please sign in to comment.