Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+92
View File
@@ -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;
}
+13
View File
@@ -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;
}
+42
View File
@@ -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;
}
+68
View File
@@ -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;
}
+153
View File
@@ -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;
}
+194
View File
@@ -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;
}
+35
View File
@@ -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;
}
+6
View File
@@ -0,0 +1,6 @@
varying vec2 uv;
uniform sampler2D texture;
void main() {
gl_FragColor = texture2D(texture,uv);
}
+8
View File
@@ -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;
}
+6
View File
@@ -0,0 +1,6 @@
varying vec2 uv;
uniform sampler2D texture;
void main() {
gl_FragColor = texture2D(texture,uv) * gl_Color;
}
+8
View File
@@ -0,0 +1,8 @@
varying vec2 uv;
void main() {
uv = gl_MultiTexCoord0.xy;
gl_Position = ftransform();
gl_FrontColor = gl_Color;
gl_BackColor = gl_Color;
}
+14
View File
@@ -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;
}
+20
View File
@@ -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;
}
+10
View File
@@ -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;
}
+11
View File
@@ -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;
}
+7
View File
@@ -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;
}
+7
View File
@@ -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;
}
+48
View File
@@ -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;
}
+17
View File
@@ -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();
}
+12
View File
@@ -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;
}
+10
View File
@@ -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;
}
+10
View File
@@ -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;
}
+8
View File
@@ -0,0 +1,8 @@
varying vec2 uv;
void main() {
float radius = length(uv);
if(radius > 1.0)
discard;
gl_FragColor = gl_Color;
}
+8
View File
@@ -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;
}
+28
View File
@@ -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;
}
+17
View File
@@ -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;
}
+36
View File
@@ -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;
}
+17
View File
@@ -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;
}
+16
View File
@@ -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;
}
+29
View File
@@ -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;
}
+34
View File
@@ -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;
}
+6
View File
@@ -0,0 +1,6 @@
uniform sampler2D screen;
varying vec2 uv;
void main() {
gl_FragColor = texture2D(screen, uv);
}
+294
View File
@@ -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;
}
+16
View File
@@ -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;
}
+6
View File
@@ -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);
}
+81
View File
@@ -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;
}
+54
View File
@@ -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));
}
+95
View File
@@ -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;
}
+12
View File
@@ -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;
}
+8
View File
@@ -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;
}
+8
View File
@@ -0,0 +1,8 @@
varying vec2 uv;
void main() {
uv = gl_MultiTexCoord0.xy;
gl_FrontColor = gl_Color;
gl_BackColor = gl_Color;
gl_Position = ftransform();
}
+33
View File
@@ -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);
}
+8
View File
@@ -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);
}
+8
View File
@@ -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);
}
+343
View File
@@ -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;
}
+7
View File
@@ -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;
}
+25
View File
@@ -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;
}
+14
View File
@@ -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;
}
+336
View File
@@ -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;
}
+47
View File
@@ -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;
}
+153
View File
@@ -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;
}
+43
View File
@@ -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;
}
+16
View File
@@ -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;
}
+36
View File
@@ -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;
}
+165
View File
@@ -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;
}
+38
View File
@@ -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;
}
+331
View File
@@ -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;
}
+37
View File
@@ -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;
}
+20
View File
@@ -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;
}
+132
View File
@@ -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;
}
+26
View File
@@ -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;
}
+113
View File
@@ -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;
}
+40
View File
@@ -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;
}
+93
View File
@@ -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;
}
+87
View File
@@ -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;
}
+104
View File
@@ -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;
}
+23
View File
@@ -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;
}
+12
View File
@@ -0,0 +1,12 @@
#version 120
varying vec2 uv;
uniform sampler2D texture;
varying vec3 pos, normal;
void main() {
vec3 n = normalize(normal);
vec3 p = vec3(0.0,0.0,-1.0);
float d = abs(dot(n,p));
gl_FragColor = texture2D(texture,vec2(uv.x,d * 0.5)) * d;
}
+43
View File
@@ -0,0 +1,43 @@
uniform vec4 curve[8];
varying vec2 uv;
varying vec3 pos, normal;
vec4 bezier(float x) {
vec4 lerp_a[7], lerp_b[6];
for(int i = 0; i < 7; ++i)
lerp_a[i] = mix(curve[i], curve[i+1], x);
for(int i = 0; i < 6; ++i)
lerp_b[i] = mix(lerp_a[i], lerp_a[i+1], x);
for(int i = 0; i < 5; ++i)
lerp_a[i] = mix(lerp_b[i], lerp_b[i+1], x);
for(int i = 0; i < 4; ++i)
lerp_b[i] = mix(lerp_a[i], lerp_a[i+1], x);
for(int i = 0; i < 3; ++i)
lerp_a[i] = mix(lerp_b[i], lerp_b[i+1], x);
for(int i = 0; i < 2; ++i)
lerp_b[i] = mix(lerp_a[i], lerp_a[i+1], x);
return mix(lerp_b[0], lerp_b[1], x);
}
//Rotate about z (x is known to be 0)
vec3 rotate(vec3 pt, float rad) {
return vec3(pt.y * -sin(rad), pt.y * cos(rad), pt.z);
}
void main() {
uv = gl_MultiTexCoord0.xy;
vec4 offset = bezier(uv.x);
vec3 n = gl_Vertex.xyz; n.x = 0.0;
n = rotate(vec3(0.0, n.yz), offset.w);
normal = normalize(gl_NormalMatrix * n);
vec3 vert = offset.xyz + rotate(vec3(0, gl_Vertex.yz), offset.w - 0.135);
vec4 postMV = gl_ModelViewMatrix * vec4(vert, gl_Vertex.w);
pos = (gl_ModelViewMatrix * offset).xyz;
gl_Position = gl_ProjectionMatrix * postMV;
}
+21
View File
@@ -0,0 +1,21 @@
#version 120
#define pi 3.141592653589793238462643383279
#define twopi (pi * 2.0)
varying vec2 rcoords;
varying vec2 uv;
uniform float progress;
uniform float dim_factor;
uniform sampler2D texture;
void main() {
vec4 color = texture2D(texture, uv) * gl_Color;
float ang = atan(rcoords.y, rcoords.x);
if(ang > -0.5 * pi)
ang = (ang + 0.5 * pi) / twopi;
else
ang = (ang + 2.5 * pi) / twopi;
if(ang > progress)
color.a *= dim_factor;
gl_FragColor = color;
}

Some files were not shown because too many files have changed in this diff Show More