344 lines
13 KiB
Plaintext
344 lines
13 KiB
Plaintext
#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;
|
|
}
|