aboutsummaryrefslogtreecommitdiff
path: root/libs/preview_nif/data/shaders/fo4_default.frag
diff options
context:
space:
mode:
Diffstat (limited to 'libs/preview_nif/data/shaders/fo4_default.frag')
-rw-r--r--libs/preview_nif/data/shaders/fo4_default.frag341
1 files changed, 341 insertions, 0 deletions
diff --git a/libs/preview_nif/data/shaders/fo4_default.frag b/libs/preview_nif/data/shaders/fo4_default.frag
new file mode 100644
index 0000000..2b1d6b8
--- /dev/null
+++ b/libs/preview_nif/data/shaders/fo4_default.frag
@@ -0,0 +1,341 @@
+#version 120
+#extension GL_ARB_shader_texture_lod : require
+
+uniform sampler2D BaseMap;
+uniform sampler2D NormalMap;
+uniform sampler2D GlowMap;
+uniform sampler2D BacklightMap;
+uniform sampler2D SpecularMap;
+uniform sampler2D GreyscaleMap;
+uniform sampler2D EnvironmentMap;
+uniform samplerCube CubeMap;
+
+uniform vec3 specColor;
+uniform float specStrength;
+uniform float specGlossiness; // "Smoothness" in FO4; 0-1
+uniform float fresnelPower;
+
+uniform float paletteScale;
+
+uniform vec3 glowColor;
+uniform float glowMult;
+
+uniform float alpha;
+
+uniform vec3 tintColor;
+
+uniform vec2 uvScale;
+uniform vec2 uvOffset;
+
+uniform bool hasEmit;
+uniform bool hasGlowMap;
+uniform bool hasSoftlight;
+uniform bool hasBacklight;
+uniform bool hasRimlight;
+uniform bool hasTintColor;
+uniform bool hasCubeMap;
+uniform bool hasEnvMask;
+uniform bool hasSpecularMap;
+uniform bool greyscaleColor;
+uniform bool doubleSided;
+
+uniform float subsurfaceRolloff;
+uniform float rimPower;
+uniform float backlightPower;
+
+uniform float envReflection;
+
+uniform mat4 modelViewMatrixInverse;
+uniform mat4 worldMatrix;
+
+varying vec2 TexCoord;
+varying vec3 LightDir;
+varying vec3 ViewDir;
+
+varying vec4 A;
+varying vec4 C;
+varying vec4 D;
+
+varying vec3 N;
+varying vec3 t;
+varying vec3 b;
+
+#ifndef M_PI
+ #define M_PI 3.1415926535897932384626433832795
+#endif
+
+#define FLT_EPSILON 1.192092896e-07F // smallest such that 1.0 + FLT_EPSILON != 1.0
+
+float OrenNayar( vec3 L, vec3 V, vec3 N, float roughness, float NdotL )
+{
+ //float NdotL = dot(N, L);
+ float NdotV = dot(N, V);
+ float LdotV = dot(L, V);
+
+ float rough2 = roughness * roughness;
+
+ float A = 1.0 - 0.5 * (rough2 / (rough2 + 0.57));
+ float B = 0.45 * (rough2 / (rough2 + 0.09));
+
+ float a = min( NdotV, NdotL );
+ float b = max( NdotV, NdotL );
+ b = (sign(b) == 0.0) ? FLT_EPSILON : sign(b) * max( 0.01, abs(b) ); // For fudging the smoothness of C
+ float C = sqrt( (1.0 - a * a) * (1.0 - b * b) ) / b;
+
+ float gamma = LdotV - NdotL * NdotV;
+ float L1 = A + B * max( gamma, FLT_EPSILON ) * C;
+
+ return L1 * max( NdotL, FLT_EPSILON );
+}
+
+float OrenNayarFull( vec3 L, vec3 V, vec3 N, float roughness, float NdotL )
+{
+ //float NdotL = dot(N, L);
+ float NdotV = dot(N, V);
+ float LdotV = dot(L, V);
+
+ float angleVN = acos(max(NdotV, FLT_EPSILON));
+ float angleLN = acos(max(NdotL, FLT_EPSILON));
+
+ float alpha = max(angleVN, angleLN);
+ float beta = min(angleVN, angleLN);
+ float gamma = LdotV - NdotL * NdotV;
+
+ float roughnessSquared = roughness * roughness;
+ float roughnessSquared9 = (roughnessSquared / (roughnessSquared + 0.09));
+
+ // C1, C2, and C3
+ float C1 = 1.0 - 0.5 * (roughnessSquared / (roughnessSquared + 0.33));
+ float C2 = 0.45 * roughnessSquared9;
+
+ if( gamma >= 0.0 ) {
+ C2 *= sin(alpha);
+ } else {
+ C2 *= (sin(alpha) - pow((2.0 * beta) / M_PI, 3.0));
+ }
+
+ float powValue = (4.0 * alpha * beta) / (M_PI * M_PI);
+ float C3 = 0.125 * roughnessSquared9 * powValue * powValue;
+
+ // Avoid asymptote at pi/2
+ float asym = M_PI / 2.0;
+ float lim1 = asym + 0.01;
+ float lim2 = asym - 0.01;
+
+ float ab2 = (alpha + beta) / 2.0;
+
+ if ( beta >= asym && beta < lim1 )
+ beta = lim1;
+ else if ( beta < asym && beta >= lim2 )
+ beta = lim2;
+
+ if ( ab2 >= asym && ab2 < lim1 )
+ ab2 = lim1;
+ else if ( ab2 < asym && ab2 >= lim2 )
+ ab2 = lim2;
+
+ // Reflection
+ float A = gamma * C2 * tan(beta);
+ float B = (1.0 - abs(gamma)) * C3 * tan(ab2);
+
+ float L1 = max(FLT_EPSILON, NdotL) * (C1 + A + B);
+
+ // Interreflection
+ float twoBetaPi = 2.0 * beta / M_PI;
+ float L2 = 0.17 * max(FLT_EPSILON, NdotL) * (roughnessSquared / (roughnessSquared + 0.13)) * (1.0 - gamma * twoBetaPi * twoBetaPi);
+
+ return L1 + L2;
+}
+
+// Schlick's Fresnel approximation
+float fresnelSchlick( float VdotH, float F0 )
+{
+ float base = 1.0 - VdotH;
+ float exp = pow( base, fresnelPower );
+ return clamp( exp + F0 * (1.0 - exp), 0.0, 1.0 );
+}
+
+// The Torrance-Sparrow visibility factor, G
+float VisibDiv( float NdotL, float NdotV, float VdotH, float NdotH )
+{
+ float denom = max( VdotH, FLT_EPSILON );
+ float numL = min( NdotV, NdotL );
+ float numR = 2.0 * NdotH;
+ if ( denom >= (numL * numR) ) {
+ numL = (numL == NdotV) ? 1.0 : (NdotL / NdotV);
+ return (numL * numR) / denom;
+ }
+ return 1.0 / NdotV;
+}
+
+// this is a normalized Phong model used in the Torrance-Sparrow model
+vec3 TorranceSparrow(float NdotL, float NdotH, float NdotV, float VdotH, vec3 color, float power, float F0)
+{
+ // D: Normalized phong model
+ float D = ((power + 2.0) / (2.0 * M_PI)) * pow( NdotH, power );
+
+ // G: Torrance-Sparrow visibility term divided by NdotV
+ float G_NdotV = VisibDiv( NdotL, NdotV, VdotH, NdotH );
+
+ // F: Schlick's approximation
+ float F = fresnelSchlick( VdotH, F0 );
+
+ // Torrance-Sparrow:
+ // (F * G * D) / (4 * NdotL * NdotV)
+ // Division by NdotV is done in VisibDiv()
+ // and division by NdotL is removed since
+ // outgoing radiance is determined by:
+ // BRDF * NdotL * L()
+ float spec = (F * G_NdotV * D) / 4.0;
+
+ return color * spec * M_PI;
+}
+
+vec3 tonemap(vec3 x)
+{
+ float _A = 0.15;
+ float _B = 0.50;
+ float _C = 0.10;
+ float _D = 0.20;
+ float _E = 0.02;
+ float _F = 0.30;
+
+ return ((x*(_A*x+_C*_B)+_D*_E)/(x*(_A*x+_B)+_D*_F))-_E/_F;
+}
+
+vec4 colorLookup( float x, float y ) {
+
+ return texture2D( GreyscaleMap, vec2( clamp(x, 0.0, 1.0), clamp(y, 0.0, 1.0) ) );
+}
+
+void main( void )
+{
+ vec2 offset = TexCoord * uvScale + uvOffset;
+
+ vec4 baseMap = texture2D( BaseMap, offset );
+ vec4 normalMap = texture2D( NormalMap, offset );
+ vec4 specMap = texture2D( SpecularMap, offset );
+ vec4 glowMap = texture2D( GlowMap, offset );
+
+ vec3 normal = normalize(normalMap.rgb * 2.0 - 1.0);
+ // Calculate missing blue channel
+ normal.b = sqrt(1.0 - dot(normal.rg, normal.rg));
+ if ( !gl_FrontFacing && doubleSided ) {
+ normal *= -1.0;
+ }
+ // For _msn (Test with FSF1_Face)
+ //normal.z = sqrt( 1.0 - dot( normal.xy, normal.xy ) );
+
+ vec3 L = normalize(LightDir);
+ vec3 V = normalize(ViewDir);
+ vec3 R = reflect(-L, normal);
+ vec3 H = normalize( L + V );
+
+ float NdotL = dot(normal, L);
+ float NdotL0 = max( NdotL, FLT_EPSILON );
+ float NdotH = max( dot(normal, H), FLT_EPSILON );
+ float NdotV = max( dot(normal, V), FLT_EPSILON );
+ float VdotH = max( dot(V, H), FLT_EPSILON );
+ float NdotNegL = max( dot(normal, -L), FLT_EPSILON );
+
+ vec3 reflected = reflect( V, normal );
+ vec3 reflectedVS = b * reflected.x + t * reflected.y + N * reflected.z;
+ vec3 reflectedWS = vec3( worldMatrix * (modelViewMatrixInverse * vec4( reflectedVS, 0.0 )) );
+
+ vec4 color;
+ vec3 albedo = baseMap.rgb * C.rgb;
+ vec3 diffuse = A.rgb + D.rgb * NdotL0;
+ if ( greyscaleColor ) {
+ vec4 luG = colorLookup( baseMap.g, paletteScale - (1 - C.r) );
+
+ albedo = luG.rgb;
+ }
+
+ // Emissive
+ vec3 emissive = vec3(0.0);
+ if ( hasEmit ) {
+ emissive += glowColor * glowMult;
+
+ if ( hasGlowMap ) {
+ emissive *= glowMap.rgb;
+ }
+ }
+
+ // Specular
+ float g = 1.0;
+ float s = 1.0;
+ float smoothness = clamp( specGlossiness, 0.0, 1.0 );
+ float specMask = 1.0;
+ vec3 spec = vec3(0.0);
+ if ( hasSpecularMap ) {
+ g = specMap.g;
+ s = specMap.r;
+ smoothness = g * smoothness;
+ float fSpecularPower = exp2( smoothness * 10 + 1 );
+ specMask = s * specStrength;
+
+ spec = TorranceSparrow( NdotL0, NdotH, NdotV, VdotH, vec3(specMask), fSpecularPower, 0.2 ) * NdotL0 * D.rgb * specColor;
+ }
+
+ // Environment
+ vec4 cube = textureCubeLod( CubeMap, reflectedWS, 8.0 - smoothness * 8.0 );
+ vec4 env = texture2D( EnvironmentMap, offset );
+ if ( hasCubeMap ) {
+ cube.rgb *= envReflection * specStrength;
+ if ( hasEnvMask ) {
+ cube.rgb *= env.r;
+ } else {
+ cube.rgb *= s;
+ }
+
+ spec += cube.rgb * diffuse;
+ }
+
+ vec3 backlight = vec3(0.0);
+ if ( backlightPower > 0.0 ) {
+ backlight = albedo * NdotNegL * clamp( backlightPower, 0.0, 1.0 );
+
+ emissive += backlight * D.rgb;
+ }
+
+ vec3 rim = vec3(0.0);
+ if ( hasRimlight ) {
+ rim = vec3(pow((1.0 - NdotV), rimPower));
+ rim *= smoothstep( -0.2, 1.0, dot(-L, V) );
+
+ //emissive += rim * D.rgb * specMask;
+ }
+
+ // Diffuse
+ float diff = OrenNayarFull( L, V, normal, 1.0 - smoothness, NdotL );
+ diffuse = vec3(diff);
+
+ vec3 soft = vec3(0.0);
+ float wrap = NdotL;
+ if ( hasSoftlight || subsurfaceRolloff > 0.0 ) {
+ wrap = (wrap + subsurfaceRolloff) / (1.0 + subsurfaceRolloff);
+ soft = albedo * max( 0.0, wrap ) * smoothstep( 1.0, 0.0, sqrt(diff) );
+
+ diffuse += soft;
+ }
+
+ if ( hasTintColor ) {
+ albedo *= tintColor;
+ }
+
+ // Diffuse
+ color.rgb = diffuse * albedo * D.rgb;
+ // Ambient
+ color.rgb += A.rgb * albedo;
+ // Specular
+ color.rgb += spec;
+ color.rgb += A.rgb * specMask * fresnelSchlick( VdotH, 0.2 ) * (1.0 - NdotV) * D.rgb;
+ // Emissive
+ color.rgb += emissive;
+
+ color.rgb = tonemap( color.rgb ) / tonemap( vec3(1.0) );
+ color.a = C.a * baseMap.a;
+
+ gl_FragColor = color;
+ gl_FragColor.a *= alpha;
+}