aboutsummaryrefslogtreecommitdiff
path: root/libs/preview_nif/data
diff options
context:
space:
mode:
Diffstat (limited to 'libs/preview_nif/data')
-rw-r--r--libs/preview_nif/data/shaders/NIFSKOPE LICENSE.md27
-rw-r--r--libs/preview_nif/data/shaders/default.vert50
-rw-r--r--libs/preview_nif/data/shaders/fo4_default.frag341
-rw-r--r--libs/preview_nif/data/shaders/fo4_effectshader.frag148
-rw-r--r--libs/preview_nif/data/shaders/sk_default.frag180
-rw-r--r--libs/preview_nif/data/shaders/sk_effectshader.frag101
-rw-r--r--libs/preview_nif/data/shaders/sk_effectshader.vert46
-rw-r--r--libs/preview_nif/data/shaders/sk_msn.frag194
-rw-r--r--libs/preview_nif/data/shaders/sk_msn.vert41
-rw-r--r--libs/preview_nif/data/shaders/sk_multilayer.frag216
10 files changed, 1344 insertions, 0 deletions
diff --git a/libs/preview_nif/data/shaders/NIFSKOPE LICENSE.md b/libs/preview_nif/data/shaders/NIFSKOPE LICENSE.md
new file mode 100644
index 0000000..d038fbe
--- /dev/null
+++ b/libs/preview_nif/data/shaders/NIFSKOPE LICENSE.md
@@ -0,0 +1,27 @@
+NIFSKOPE LICENSE
+
+Copyright (c) 2005-2014, NIF File Format Library and Tools.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the NIF File Format Library and Tools project may not be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/libs/preview_nif/data/shaders/default.vert b/libs/preview_nif/data/shaders/default.vert
new file mode 100644
index 0000000..90cccd3
--- /dev/null
+++ b/libs/preview_nif/data/shaders/default.vert
@@ -0,0 +1,50 @@
+#version 120
+
+uniform mat4 modelViewMatrix;
+uniform mat4 mvpMatrix;
+uniform mat3 normalMatrix;
+uniform vec3 lightDirection;
+uniform vec4 ambientColor;
+uniform vec4 diffuseColor;
+
+attribute vec3 position;
+attribute vec3 normal;
+attribute vec3 tangent;
+attribute vec3 bitangent;
+attribute vec2 texCoord;
+attribute vec4 color;
+
+varying vec2 TexCoord;
+varying vec3 LightDir;
+varying vec3 ViewDir;
+
+varying vec3 N;
+varying vec3 t;
+varying vec3 b;
+varying vec3 v;
+
+varying vec4 A;
+varying vec4 C;
+varying vec4 D;
+
+void main( void )
+{
+ gl_Position = mvpMatrix * vec4(position, 1.0);
+ TexCoord = texCoord;
+
+ N = normalize(normalMatrix * normal);
+ t = normalize(normalMatrix * tangent);
+ b = normalize(normalMatrix * bitangent);
+ v = vec3(modelViewMatrix * vec4(position, 1.0));
+
+ mat3 tbnMatrix = mat3(b.x, t.x, N.x,
+ b.y, t.y, N.y,
+ b.z, t.z, N.z);
+
+ ViewDir = tbnMatrix * -v;
+ LightDir = tbnMatrix * lightDirection;
+
+ A = ambientColor;
+ C = color;
+ D = diffuseColor;
+}
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;
+}
diff --git a/libs/preview_nif/data/shaders/fo4_effectshader.frag b/libs/preview_nif/data/shaders/fo4_effectshader.frag
new file mode 100644
index 0000000..31b241f
--- /dev/null
+++ b/libs/preview_nif/data/shaders/fo4_effectshader.frag
@@ -0,0 +1,148 @@
+#version 120
+
+uniform sampler2D BaseMap;
+uniform sampler2D GreyscaleMap;
+uniform samplerCube CubeMap;
+uniform sampler2D NormalMap;
+uniform sampler2D SpecularMap;
+
+uniform bool doubleSided;
+
+uniform bool hasSourceTexture;
+uniform bool hasGreyscaleMap;
+uniform bool hasCubeMap;
+uniform bool hasNormalMap;
+uniform bool hasEnvMask;
+
+uniform bool greyscaleAlpha;
+uniform bool greyscaleColor;
+
+uniform bool useFalloff;
+uniform bool hasRGBFalloff;
+
+uniform bool hasWeaponBlood;
+
+uniform vec4 glowColor;
+uniform float glowMult;
+
+uniform vec2 uvScale;
+uniform vec2 uvOffset;
+
+uniform vec4 falloffParams;
+uniform float falloffDepth;
+
+uniform float lightingInfluence;
+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;
+varying vec3 v;
+
+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 );
+
+ 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;
+ }
+
+ vec3 L = normalize(LightDir);
+ vec3 V = normalize(ViewDir);
+ vec3 R = reflect(-L, normal);
+ vec3 H = normalize( L + V );
+
+ float NdotL = max( dot(normal, L), 0.000001 );
+ float NdotH = max( dot(normal, H), 0.000001 );
+ float NdotV = max( dot(normal, V), 0.000001 );
+ float LdotH = max( dot(L, H), 0.000001 );
+ float NdotNegL = max( dot(normal, -L), 0.000001 );
+
+ 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 )) );
+
+ if ( greyscaleAlpha )
+ baseMap.a = 1.0;
+
+ vec4 baseColor = glowColor;
+ if ( !greyscaleColor )
+ baseColor.rgb *= glowMult;
+
+ // Falloff
+ float falloff = 1.0;
+ if ( useFalloff || hasRGBFalloff ) {
+ falloff = smoothstep( falloffParams.x, falloffParams.y, abs(dot(normal, V)) );
+ falloff = mix( max(falloffParams.z, 0.0), min(falloffParams.w, 1.0), falloff );
+
+ if ( useFalloff )
+ baseMap.a *= falloff;
+
+ if ( hasRGBFalloff )
+ baseMap.rgb *= falloff;
+ }
+
+ float alphaMult = baseColor.a * baseColor.a;
+
+ vec4 color;
+ color.rgb = baseMap.rgb * C.rgb * baseColor.rgb;
+ color.a = alphaMult * C.a * baseMap.a;
+
+ if ( greyscaleColor ) {
+ vec4 luG = colorLookup( texture2D( BaseMap, offset ).g, baseColor.r * C.r * falloff );
+
+ color.rgb = luG.rgb;
+ }
+
+ if ( greyscaleAlpha ) {
+ vec4 luA = colorLookup( texture2D( BaseMap, offset ).a, color.a );
+
+ color.a = luA.a;
+ }
+
+ vec3 diffuse = A.rgb + (D.rgb * NdotL);
+ color.rgb = mix( color.rgb, color.rgb * D.rgb, lightingInfluence );
+
+ // Specular
+ float g = 1.0;
+ float s = 1.0;
+ if ( hasEnvMask ) {
+ g = specMap.r;
+ s = specMap.g;
+ }
+
+ // Environment
+ vec4 cube = textureCube( CubeMap, reflectedWS );
+ if ( hasCubeMap ) {
+ cube.rgb *= envReflection * s;
+ cube.rgb = mix( cube.rgb, cube.rgb * D.rgb, lightingInfluence );
+
+ color.rgb += cube.rgb * falloff;
+ }
+
+ gl_FragColor.rgb = color.rgb;
+ gl_FragColor.a = color.a;
+}
diff --git a/libs/preview_nif/data/shaders/sk_default.frag b/libs/preview_nif/data/shaders/sk_default.frag
new file mode 100644
index 0000000..7cd0dcf
--- /dev/null
+++ b/libs/preview_nif/data/shaders/sk_default.frag
@@ -0,0 +1,180 @@
+#version 120
+
+uniform sampler2D BaseMap;
+uniform sampler2D NormalMap;
+uniform sampler2D GlowMap;
+uniform sampler2D HeightMap;
+uniform sampler2D LightMask;
+uniform sampler2D BacklightMap;
+uniform sampler2D EnvironmentMap;
+uniform samplerCube CubeMap;
+
+uniform vec3 specColor;
+uniform float specStrength;
+uniform float specGlossiness;
+
+uniform bool hasGlowMap;
+uniform vec3 glowColor;
+uniform float glowMult;
+
+uniform float alpha;
+
+uniform vec3 tintColor;
+
+uniform bool hasHeightMap;
+uniform vec2 uvScale;
+uniform vec2 uvOffset;
+
+uniform bool hasEmit;
+uniform bool hasSoftlight;
+uniform bool hasBacklight;
+uniform bool hasRimlight;
+uniform bool hasTintColor;
+uniform bool hasCubeMap;
+uniform bool hasEnvMask;
+
+uniform float softlight;
+uniform float rimPower;
+
+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;
+
+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;
+}
+
+vec3 toGrayscale(vec3 color)
+{
+ return vec3(dot(vec3(0.3, 0.59, 0.11), color));
+}
+
+void main( void )
+{
+ vec2 offset = TexCoord * uvScale + uvOffset;
+
+ vec3 E = normalize(ViewDir);
+
+ if ( hasHeightMap ) {
+ float height = texture2D( HeightMap, offset ).r;
+ offset += E.xy * (height * 0.08 - 0.04);
+ }
+
+ vec4 baseMap = texture2D( BaseMap, offset );
+ vec4 normalMap = texture2D( NormalMap, offset );
+ vec4 glowMap = texture2D( GlowMap, offset );
+
+ vec3 normal = normalize(normalMap.rgb * 2.0 - 1.0);
+
+ vec3 L = normalize(LightDir);
+ vec3 R = reflect(-L, normal);
+ vec3 H = normalize( L + E );
+
+ float NdotL = max( dot(normal, L), 0.0 );
+ float NdotH = max( dot(normal, H), 0.0 );
+ float EdotN = max( dot(normal, E), 0.0 );
+ float NdotNegL = max( dot(normal, -L), 0.0 );
+
+ vec3 reflected = reflect( -E, 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 * NdotL);
+
+
+ // Environment
+ if ( hasCubeMap ) {
+ vec4 cube = textureCube( CubeMap, reflectedWS );
+ cube.rgb *= envReflection;
+
+ if ( hasEnvMask ) {
+ vec4 env = texture2D( EnvironmentMap, offset );
+ cube.rgb *= env.r;
+ } else {
+ cube.rgb *= normalMap.a;
+ }
+
+
+ albedo += cube.rgb;
+ }
+
+ // Emissive & Glow
+ vec3 emissive = vec3(0.0);
+ if ( hasEmit ) {
+ emissive += glowColor * glowMult;
+
+ if ( hasGlowMap ) {
+ emissive *= glowMap.rgb;
+ }
+ }
+
+ // Specular
+ vec3 spec = clamp( specColor * specStrength * normalMap.a * pow(NdotH, specGlossiness), 0.0, 1.0 );
+ spec *= D.rgb;
+
+ vec3 backlight = vec3(0.0);
+ if ( hasBacklight ) {
+ backlight = texture2D( BacklightMap, offset ).rgb;
+ backlight *= NdotNegL;
+
+ emissive += backlight * D.rgb;
+ }
+
+ vec4 mask = vec4(0.0);
+ if ( hasRimlight || hasSoftlight ) {
+ mask = texture2D( LightMask, offset );
+ }
+
+ vec3 rim = vec3(0.0);
+ if ( hasRimlight ) {
+ rim = mask.rgb * pow(vec3((1.0 - EdotN)), vec3(rimPower));
+ rim *= smoothstep( -0.2, 1.0, dot(-L, E) );
+
+ emissive += rim * D.rgb;
+ }
+
+ vec3 soft = vec3(0.0);
+ if ( hasSoftlight ) {
+ float wrap = (dot(normal, L) + softlight) / (1.0 + softlight);
+
+ soft = max( wrap, 0.0 ) * mask.rgb * smoothstep( 1.0, 0.0, NdotL );
+ soft *= sqrt( clamp( softlight, 0.0, 1.0 ) );
+
+ emissive += soft * D.rgb;
+ }
+
+ if ( hasTintColor ) {
+ albedo *= tintColor;
+ }
+
+ color.rgb = albedo * (diffuse + emissive) + spec;
+ color.rgb = tonemap( color.rgb ) / tonemap( vec3(1.0) );
+ color.a = C.a * baseMap.a;
+
+ gl_FragColor = color;
+ gl_FragColor.a *= alpha;
+}
diff --git a/libs/preview_nif/data/shaders/sk_effectshader.frag b/libs/preview_nif/data/shaders/sk_effectshader.frag
new file mode 100644
index 0000000..3a22388
--- /dev/null
+++ b/libs/preview_nif/data/shaders/sk_effectshader.frag
@@ -0,0 +1,101 @@
+#version 120
+
+uniform sampler2D BaseMap;
+uniform sampler2D GreyscaleMap;
+
+uniform bool doubleSided;
+
+uniform bool hasSourceTexture;
+uniform bool hasGreyscaleMap;
+uniform bool greyscaleAlpha;
+uniform bool greyscaleColor;
+
+uniform bool useFalloff;
+uniform bool vertexColors;
+uniform bool vertexAlpha;
+
+uniform bool hasWeaponBlood;
+
+uniform vec4 glowColor;
+uniform float glowMult;
+
+uniform vec2 uvScale;
+uniform vec2 uvOffset;
+
+uniform vec4 falloffParams;
+uniform float falloffDepth;
+
+varying vec2 TexCoord;
+varying vec3 LightDir;
+varying vec3 ViewDir;
+
+varying vec4 C;
+
+varying vec3 N;
+varying vec3 v;
+
+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 )
+{
+ vec4 baseMap = texture2D( BaseMap, TexCoord.st * uvScale + uvOffset );
+
+ vec4 color;
+
+ vec3 normal = N;
+
+ // Reconstructed normal
+ //normal = normalize(cross(dFdy(v.xyz), dFdx(v.xyz)));
+
+ //if ( !doubleSided && !gl_FrontFacing ) { return; }
+
+ vec3 E = normalize(ViewDir);
+
+ float tmp2 = falloffDepth; // Unused right now
+
+ // Falloff
+ float falloff = 1.0;
+ if ( useFalloff ) {
+ float startO = min(falloffParams.z, 1.0);
+ float stopO = max(falloffParams.w, 0.0);
+
+ // TODO: When X and Y are both 0.0 or both 1.0 the effect is reversed.
+ falloff = smoothstep( falloffParams.y, falloffParams.x, abs(E.b));
+
+ falloff = mix( max(falloffParams.w, 0.0), min(falloffParams.z, 1.0), falloff );
+ }
+
+ float alphaMult = glowColor.a * glowColor.a;
+
+ color.rgb = baseMap.rgb;
+ color.a = baseMap.a;
+
+ if ( hasWeaponBlood ) {
+ color.rgb = vec3( 1.0, 0.0, 0.0 ) * baseMap.r;
+ color.a = baseMap.a * baseMap.g;
+ }
+
+ color.rgb *= C.rgb * glowColor.rgb;
+ color.a *= C.a * falloff * alphaMult;
+
+ if ( greyscaleColor ) {
+ // Only Red emissive channel is used
+ float emRGB = glowColor.r;
+
+ vec4 luG = colorLookup( baseMap.g, C.g * falloff * emRGB );
+
+ color.rgb = luG.rgb;
+ }
+
+ if ( greyscaleAlpha ) {
+ vec4 luA = colorLookup( baseMap.a, C.a * falloff * alphaMult );
+
+ color.a = luA.a;
+ }
+
+ gl_FragColor.rgb = color.rgb * glowMult;
+ gl_FragColor.a = color.a;
+}
diff --git a/libs/preview_nif/data/shaders/sk_effectshader.vert b/libs/preview_nif/data/shaders/sk_effectshader.vert
new file mode 100644
index 0000000..3204cba
--- /dev/null
+++ b/libs/preview_nif/data/shaders/sk_effectshader.vert
@@ -0,0 +1,46 @@
+#version 120
+
+uniform mat4 modelViewMatrix;
+uniform mat4 mvpMatrix;
+uniform mat3 normalMatrix;
+uniform vec3 lightDirection;
+uniform vec4 ambientColor;
+uniform vec4 diffuseColor;
+
+attribute vec3 position;
+attribute vec3 normal;
+attribute vec3 tangent;
+attribute vec3 bitangent;
+attribute vec2 texCoord;
+attribute vec4 color;
+
+varying vec2 TexCoord;
+varying vec3 LightDir;
+varying vec3 ViewDir;
+
+varying vec4 C;
+
+varying vec3 N;
+varying vec3 t;
+varying vec3 b;
+varying vec3 v;
+
+void main( void )
+{
+ gl_Position = mvpMatrix * vec4(position, 1);
+ TexCoord = texCoord;
+
+ N = normalize(normalMatrix * normal);
+ t = normalize(normalMatrix * tangent);
+ b = normalize(normalMatrix * bitangent);
+ v = vec3(modelViewMatrix * vec4(position, 1));
+
+ mat3 tbnMatrix = mat3(b.x, t.x, N.x,
+ b.y, t.y, N.y,
+ b.z, t.z, N.z);
+
+ ViewDir = tbnMatrix * -v.xyz;
+ LightDir = tbnMatrix * lightDirection.xyz;
+
+ C = color;
+}
diff --git a/libs/preview_nif/data/shaders/sk_msn.frag b/libs/preview_nif/data/shaders/sk_msn.frag
new file mode 100644
index 0000000..3ebbf93
--- /dev/null
+++ b/libs/preview_nif/data/shaders/sk_msn.frag
@@ -0,0 +1,194 @@
+#version 120
+
+uniform sampler2D BaseMap;
+uniform sampler2D NormalMap;
+uniform sampler2D SpecularMap;
+uniform sampler2D LightMask;
+uniform sampler2D TintMask;
+uniform sampler2D DetailMask;
+uniform sampler2D BacklightMap;
+
+uniform vec3 specColor;
+uniform float specStrength;
+uniform float specGlossiness;
+
+uniform vec3 glowColor;
+uniform float glowMult;
+
+uniform float alpha;
+
+uniform vec3 tintColor;
+
+uniform vec2 uvScale;
+uniform vec2 uvOffset;
+
+uniform bool hasEmit;
+uniform bool hasSoftlight;
+uniform bool hasBacklight;
+uniform bool hasRimlight;
+uniform bool hasSpecularMap;
+uniform bool hasDetailMask;
+uniform bool hasTintMask;
+uniform bool hasTintColor;
+
+uniform float softlight;
+uniform float rimPower;
+
+uniform mat4 viewMatrix;
+
+varying vec3 v;
+
+varying vec2 TexCoord;
+varying vec3 LightDir;
+varying vec3 ViewDir;
+
+varying vec4 A;
+varying vec4 C;
+varying vec4 D;
+
+
+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;
+}
+
+vec3 toGrayscale(vec3 color)
+{
+ return vec3(dot(vec3(0.3, 0.59, 0.11), color));
+}
+
+float overlay( float base, float blend )
+{
+ float result;
+ if ( base < 0.5 ) {
+ result = 2.0 * base * blend;
+ } else {
+ result = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);
+ }
+ return result;
+}
+
+vec3 overlay( vec3 ba, vec3 bl )
+{
+ return vec3( overlay(ba.r, bl.r), overlay(ba.g, bl.g), overlay( ba.b, bl.b ) );
+}
+
+void main( void )
+{
+ vec2 offset = TexCoord.st * uvScale + uvOffset;
+
+ vec4 baseMap = texture2D( BaseMap, offset );
+ vec4 normalMap = texture2D( NormalMap, offset );
+
+ vec3 normal = normalMap.rgb * 2.0 - 1.0;
+
+ // Convert model space to view space
+ // Swizzled G/B values!
+ normal = normalize( vec3( viewMatrix * vec4( normal.rbg, 0.0 )));
+
+ // Face Normals
+ //vec3 X = dFdx(v);
+ //vec3 Y = dFdy(v);
+ //vec3 constructedNormal = normalize(cross(X,Y));
+
+
+ vec3 L = normalize(LightDir);
+ vec3 E = normalize(ViewDir);
+ vec3 R = reflect(-L, normal);
+ vec3 H = normalize( L + E );
+
+ float NdotL = max( dot(normal, L), 0.0 );
+ float NdotH = max( dot(normal, H), 0.0 );
+ float EdotN = max( dot(normal, E), 0.0 );
+ float NdotNegL = max( dot(normal, -L), 0.0 );
+
+
+ vec4 color;
+ vec3 albedo = baseMap.rgb * C.rgb;
+ vec3 diffuse = A.rgb + (D.rgb * NdotL);
+
+
+ // Emissive
+ vec3 emissive = vec3(0.0);
+ if ( hasEmit ) {
+ emissive += glowColor * glowMult;
+ }
+
+ // Specular
+
+ float s = texture2D( SpecularMap, offset ).r;
+ if ( !hasSpecularMap || hasBacklight ) {
+ s = normalMap.a;
+ }
+
+ vec3 spec = clamp( specColor * specStrength * s * pow(NdotH, specGlossiness), 0.0, 1.0 );
+ spec *= D.rgb;
+
+
+ vec3 backlight = vec3(0.0);
+ if ( hasBacklight ) {
+ backlight = texture2D( BacklightMap, offset ).rgb;
+ backlight *= NdotNegL;
+
+ emissive += backlight * D.rgb;
+ }
+
+ vec4 mask = vec4(0.0);
+ if ( hasRimlight || hasSoftlight ) {
+ mask = texture2D( LightMask, offset );
+ }
+
+ vec3 rim = vec3(0.0);
+ if ( hasRimlight ) {
+ rim = mask.rgb * pow(vec3((1.0 - EdotN)), vec3(rimPower));
+ rim *= smoothstep( -0.2, 1.0, dot(-L, E) );
+
+ emissive += rim * D.rgb;
+ }
+
+ vec3 soft = vec3(0.0);
+ if ( hasSoftlight ) {
+ float wrap = (dot(normal, L) + softlight) / (1.0 + softlight);
+
+ soft = max( wrap, 0.0 ) * mask.rgb * smoothstep( 1.0, 0.0, NdotL );
+ soft *= sqrt( clamp( softlight, 0.0, 1.0 ) );
+
+ emissive += soft * D.rgb;
+ }
+
+ vec3 detail = vec3(0.0);
+ if ( hasDetailMask ) {
+ detail = texture2D( DetailMask, offset ).rgb;
+
+ albedo = overlay( albedo, detail );
+ }
+
+ vec3 tint = vec3(0.0);
+ if ( hasTintMask ) {
+ tint = texture2D( TintMask, offset ).rgb;
+
+ albedo = overlay( albedo, tint );
+ }
+
+ if ( hasDetailMask ) {
+ albedo += albedo;
+ }
+
+ if ( hasTintColor ) {
+ albedo *= tintColor;
+ }
+
+ color.rgb = albedo * (diffuse + emissive) + spec;
+ color.rgb = tonemap( color.rgb ) / tonemap( vec3(1.0) );
+ color.a = C.a * baseMap.a;
+
+ gl_FragColor = color;
+ gl_FragColor.a *= alpha;
+}
diff --git a/libs/preview_nif/data/shaders/sk_msn.vert b/libs/preview_nif/data/shaders/sk_msn.vert
new file mode 100644
index 0000000..755605f
--- /dev/null
+++ b/libs/preview_nif/data/shaders/sk_msn.vert
@@ -0,0 +1,41 @@
+#version 120
+
+uniform mat4 modelViewMatrix;
+uniform mat4 mvpMatrix;
+uniform mat3 normalMatrix;
+uniform vec3 lightDirection;
+uniform vec4 ambientColor;
+uniform vec4 diffuseColor;
+
+attribute vec3 position;
+attribute vec3 normal;
+attribute vec3 tangent;
+attribute vec3 bitangent;
+attribute vec2 texCoord;
+attribute vec4 color;
+
+varying vec2 TexCoord;
+varying vec3 LightDir;
+varying vec3 ViewDir;
+
+varying vec3 v;
+
+varying vec4 A;
+varying vec4 C;
+varying vec4 D;
+
+
+void main( void )
+{
+ gl_Position = mvpMatrix * vec4(position, 1);
+ TexCoord = texCoord;
+
+ v = vec3(modelViewMatrix * vec4(position, 1));
+
+ ViewDir = -v.xyz;
+ LightDir = lightDirection;
+
+ A = ambientColor;
+ C = color;
+ D = diffuseColor;
+}
diff --git a/libs/preview_nif/data/shaders/sk_multilayer.frag b/libs/preview_nif/data/shaders/sk_multilayer.frag
new file mode 100644
index 0000000..fc4afdb
--- /dev/null
+++ b/libs/preview_nif/data/shaders/sk_multilayer.frag
@@ -0,0 +1,216 @@
+#version 120
+
+uniform sampler2D BaseMap;
+uniform sampler2D NormalMap;
+uniform sampler2D LightMask;
+uniform sampler2D BacklightMap;
+uniform sampler2D InnerMap;
+uniform sampler2D EnvironmentMap;
+uniform samplerCube CubeMap;
+
+uniform vec3 specColor;
+uniform float specStrength;
+uniform float specGlossiness;
+
+uniform vec3 glowColor;
+uniform float glowMult;
+
+uniform float alpha;
+
+uniform vec2 uvScale;
+uniform vec2 uvOffset;
+
+uniform bool hasEmit;
+uniform bool hasSoftlight;
+uniform bool hasBacklight;
+uniform bool hasRimlight;
+uniform bool hasCubeMap;
+uniform bool hasEnvMask;
+
+uniform float softlight;
+uniform float rimPower;
+
+uniform vec2 innerScale;
+uniform float innerThickness;
+uniform float outerRefraction;
+uniform float outerReflection;
+
+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;
+
+
+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;
+}
+
+vec3 toGrayscale(vec3 color)
+{
+ return vec3(dot(vec3(0.3, 0.59, 0.11), color));
+}
+
+// Compute inner layer�s texture coordinate and transmission depth
+// vTexCoord: Outer layer�s texture coordinate
+// vInnerScale: Tiling of inner texture
+// vViewTS: View vector in tangent space
+// vNormalTS: Normal in tangent space (sampled normal map)
+// fLayerThickness: Distance from outer layer to inner layer
+vec3 ParallaxOffsetAndDepth( vec2 vTexCoord, vec2 vInnerScale, vec3 vViewTS, vec3 vNormalTS, float fLayerThickness )
+{
+ // Tangent space reflection vector
+ vec3 vReflectionTS = reflect( -vViewTS, vNormalTS );
+ // Tangent space transmission vector (reflect about surface plane)
+ vec3 vTransTS = vec3( vReflectionTS.xy, -vReflectionTS.z );
+
+ // Distance along transmission vector to intersect inner layer
+ float fTransDist = fLayerThickness / abs(vTransTS.z);
+
+ // Texel size
+ // Bethesda's version does indeed seem to assume 1024, which is why they
+ // introduced the additional parameter.
+ vec2 vTexelSize = vec2( 1.0/(1024.0 * vInnerScale.x), 1.0/(1024.0 * vInnerScale.y) );
+
+ // Inner layer�s texture coordinate due to parallax
+ vec2 vOffset = vTexelSize * fTransDist * vTransTS.xy;
+ vec2 vOffsetTexCoord = vTexCoord + vOffset;
+
+ // Return offset texture coordinate in xy and transmission dist in z
+ return vec3( vOffsetTexCoord, fTransDist );
+}
+
+void main( void )
+{
+ vec2 offset = TexCoord * uvScale + uvOffset;
+
+ vec4 baseMap = texture2D( BaseMap, offset );
+ vec4 normalMap = texture2D( NormalMap, offset );
+
+ vec3 normal = normalize(normalMap.rgb * 2.0 - 1.0);
+
+ // Sample the non-parallax offset alpha channel of the inner map
+ // Used to modulate the innerThickness
+ float innerMapAlpha = texture2D( InnerMap, offset ).a;
+
+
+ vec3 L = normalize(LightDir);
+ vec3 E = normalize(ViewDir);
+ vec3 R = reflect(-L, normal);
+ vec3 H = normalize( L + E );
+
+ float NdotL = max( dot(normal, L), 0.0 );
+ float NdotH = max( dot(normal, H), 0.0 );
+ float EdotN = max( dot(normal, E), 0.0 );
+ float NdotNegL = max( dot(normal, -L), 0.0 );
+
+
+ // Mix between the face normal and the normal map based on the refraction scale
+ vec3 mixedNormal = mix( vec3(0.0, 0.0, 1.0), normal, clamp( outerRefraction, 0.0, 1.0 ) );
+ vec3 parallax = ParallaxOffsetAndDepth( offset, innerScale, E, mixedNormal, innerThickness * innerMapAlpha );
+
+ // Sample the inner map at the offset coords
+ vec4 innerMap = texture2D( InnerMap, parallax.xy * innerScale );
+
+ vec3 reflected = reflect( -E, 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;
+ vec3 diffuse = A.rgb + (D.rgb * NdotL);
+ vec3 inner = innerMap.rgb * C.rgb;
+ vec3 outer = baseMap.rgb * C.rgb;
+
+
+ // Mix inner/outer layer based on fresnel
+ float outerMix = max( 1.0 - EdotN, baseMap.a );
+ albedo = mix( inner, outer, outerMix );
+
+
+ // Environment
+ if ( hasCubeMap ) {
+ vec4 cube = textureCube( CubeMap, reflectedWS );
+ cube.rgb *= outerReflection;
+
+ if ( hasEnvMask ) {
+ vec4 env = texture2D( EnvironmentMap, offset );
+ cube.rgb *= env.r;
+ } else {
+ cube.rgb *= normalMap.a;
+ }
+
+ albedo += cube.rgb;
+ }
+
+ // Specular
+ vec3 spec = clamp( specColor * specStrength * normalMap.a * pow(NdotH, specGlossiness), 0.0, 1.0 );
+ spec *= D.rgb;
+
+ // Emissive
+ // Mixed with outer map
+ vec3 emissive = vec3(0.0);
+ if ( hasEmit ) {
+ emissive += glowColor * glowMult;
+ }
+
+ // Backlight
+ // Mixed with inner and outer map
+ vec3 backlight = vec3(0.0);
+ if ( hasBacklight ) {
+ backlight = texture2D( BacklightMap, offset ).rgb;
+ backlight *= NdotNegL;
+
+ emissive += backlight * D.rgb;
+ }
+
+ // TODO: Test rim and soft light mixing with inner/outer layer
+
+ vec4 mask = vec4(0.0);
+ if ( hasRimlight || hasSoftlight ) {
+ mask = texture2D( LightMask, offset );
+ }
+
+ vec3 rim = vec3(0.0);
+ if ( hasRimlight ) {
+ rim = mask.rgb * pow(vec3((1.0 - EdotN)), vec3(rimPower));
+ rim *= smoothstep( -0.2, 1.0, dot(-L, E) );
+
+ emissive += rim * D.rgb;
+ }
+
+ vec3 soft = vec3(0.0);
+ if ( hasSoftlight ) {
+ float wrap = (dot(normal, L) + softlight) / (1.0 + softlight);
+
+ soft = max( wrap, 0.0 ) * mask.rgb * smoothstep( 1.0, 0.0, NdotL );
+ soft *= sqrt( clamp( softlight, 0.0, 1.0 ) );
+
+ emissive += soft * D.rgb;
+ }
+
+ color.rgb = albedo * (diffuse + emissive) + spec;
+ color.rgb = tonemap( color.rgb ) / tonemap( vec3(1.0) );
+ color.a = C.a * baseMap.a;
+
+ gl_FragColor = color;
+ gl_FragColor.a *= alpha;
+}