Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F629791
pbr_stations_lodding_ps.txt
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
16 KB
Referenced Files
None
Subscribers
None
pbr_stations_lodding_ps.txt
View Options
#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;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sun, Nov 15, 7:48 AM (1 d, 23 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
139939
Default Alt Text
pbr_stations_lodding_ps.txt (16 KB)
Attached To
Mode
R80 StarRuler2-Source
Attached
Detach File
Event Timeline