#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); }