mirror of
https://github.com/Ttanasart-pt/Pixel-Composer.git
synced 2025-01-14 16:26:30 +01:00
798 lines
22 KiB
Text
798 lines
22 KiB
Text
|
// FIXME: Temporary fix!
|
||
|
precision highp float;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Defines
|
||
|
//
|
||
|
|
||
|
// Maximum number of point lights
|
||
|
#define MAX_PUNCTUAL_LIGHTS 8
|
||
|
// Number of samples used when computing shadows
|
||
|
#define SHADOWMAP_SAMPLE_COUNT 12
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Varyings
|
||
|
//
|
||
|
|
||
|
varying vec3 v_vVertex;
|
||
|
|
||
|
varying vec2 v_vTexCoord;
|
||
|
varying mat3 v_mTBN;
|
||
|
varying vec4 v_vPosition;
|
||
|
|
||
|
varying vec4 v_vPosShadowmap;
|
||
|
|
||
|
varying vec2 v_vSplatmapCoord;
|
||
|
|
||
|
varying vec4 v_vEye;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Uniforms
|
||
|
//
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Material
|
||
|
|
||
|
// Material index
|
||
|
// uniform float bbmod_MaterialIndex;
|
||
|
|
||
|
// RGB: Base color, A: Opacity
|
||
|
#define bbmod_BaseOpacity gm_BaseTexture
|
||
|
|
||
|
// RGBA
|
||
|
uniform vec4 bbmod_BaseOpacityMultiplier;
|
||
|
|
||
|
// If 1.0 then the material uses roughness
|
||
|
uniform float bbmod_IsRoughness;
|
||
|
// If 1.0 then the material uses metallic workflow
|
||
|
uniform float bbmod_IsMetallic;
|
||
|
// RGB: Tangent-space normal, A: Smoothness or roughness
|
||
|
uniform sampler2D bbmod_NormalW;
|
||
|
// RGB: specular color / R: Metallic, G: ambient occlusion
|
||
|
uniform sampler2D bbmod_Material;
|
||
|
|
||
|
// Pixels with alpha less than this value will be discarded
|
||
|
uniform float bbmod_AlphaTest;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Camera
|
||
|
|
||
|
// Camera's position in world space
|
||
|
uniform vec3 bbmod_CamPos;
|
||
|
// Distance to the far clipping plane
|
||
|
uniform float bbmod_ZFar;
|
||
|
// Camera's exposure value
|
||
|
uniform float bbmod_Exposure;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Fog
|
||
|
|
||
|
// The color of the fog
|
||
|
uniform vec4 bbmod_FogColor;
|
||
|
// Maximum fog intensity
|
||
|
uniform float bbmod_FogIntensity;
|
||
|
// Distance at which the fog starts
|
||
|
uniform float bbmod_FogStart;
|
||
|
// 1.0 / (fogEnd - fogStart)
|
||
|
uniform float bbmod_FogRcpRange;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Ambient light
|
||
|
|
||
|
// RGBM encoded ambient light color on the upper hemisphere.
|
||
|
uniform vec4 bbmod_LightAmbientUp;
|
||
|
// RGBM encoded ambient light color on the lower hemisphere.
|
||
|
uniform vec4 bbmod_LightAmbientDown;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Directional light
|
||
|
|
||
|
// Direction of the directional light
|
||
|
uniform vec3 bbmod_LightDirectionalDir;
|
||
|
// RGBM encoded color of the directional light
|
||
|
uniform vec4 bbmod_LightDirectionalColor;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// SSAO
|
||
|
|
||
|
// SSAO texture
|
||
|
uniform sampler2D bbmod_SSAO;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Image based lighting
|
||
|
|
||
|
// Prefiltered octahedron env. map
|
||
|
uniform sampler2D bbmod_IBL;
|
||
|
// Texel size of one octahedron
|
||
|
uniform vec2 bbmod_IBLTexel;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Punctual lights
|
||
|
|
||
|
// [(x, y, z, range), (r, g, b, m), ...]
|
||
|
uniform vec4 bbmod_LightPunctualDataA[2 * MAX_PUNCTUAL_LIGHTS];
|
||
|
// [(isSpotLight, dcosInner, dcosOuter), (dX, dY, dZ), ...]
|
||
|
uniform vec3 bbmod_LightPunctualDataB[2 * MAX_PUNCTUAL_LIGHTS];
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Terrain
|
||
|
|
||
|
// Splatmap texture
|
||
|
uniform sampler2D bbmod_Splatmap;
|
||
|
// Splatmap channel to read. Use -1 for none.
|
||
|
uniform int bbmod_SplatmapIndex;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// Shadow mapping
|
||
|
|
||
|
// 1.0 to enable shadows
|
||
|
uniform float bbmod_ShadowmapEnablePS;
|
||
|
// Shadowmap texture
|
||
|
uniform sampler2D bbmod_Shadowmap;
|
||
|
// (1.0/shadowmapWidth, 1.0/shadowmapHeight)
|
||
|
uniform vec2 bbmod_ShadowmapTexel;
|
||
|
// The area that the shadowmap captures
|
||
|
uniform float bbmod_ShadowmapArea;
|
||
|
// The range over which meshes smoothly transition into shadow.
|
||
|
uniform float bbmod_ShadowmapBias;
|
||
|
// The index of the light that casts shadows. Use -1 for the directional light.
|
||
|
uniform float bbmod_ShadowCasterIndex;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Includes
|
||
|
//
|
||
|
struct Material
|
||
|
{
|
||
|
vec3 Base;
|
||
|
float Opacity;
|
||
|
vec3 Normal;
|
||
|
float Metallic;
|
||
|
float Roughness;
|
||
|
vec3 Specular;
|
||
|
float Smoothness;
|
||
|
float SpecularPower;
|
||
|
float AO;
|
||
|
vec3 Emissive;
|
||
|
vec4 Subsurface;
|
||
|
vec3 Lightmap;
|
||
|
};
|
||
|
|
||
|
Material CreateMaterial(mat3 TBN)
|
||
|
{
|
||
|
Material m;
|
||
|
m.Base = vec3(1.0);
|
||
|
m.Opacity = 1.0;
|
||
|
m.Normal = normalize(TBN * vec3(0.0, 0.0, 1.0));
|
||
|
m.Metallic = 0.0;
|
||
|
m.Roughness = 1.0;
|
||
|
m.Specular = vec3(0.0);
|
||
|
m.Smoothness = 0.0;
|
||
|
m.SpecularPower = 1.0;
|
||
|
m.AO = 1.0;
|
||
|
m.Emissive = vec3(0.0);
|
||
|
m.Subsurface = vec4(0.0);
|
||
|
m.Lightmap = vec3(0.0);
|
||
|
return m;
|
||
|
}
|
||
|
#define F0_DEFAULT vec3(0.04)
|
||
|
#define X_GAMMA 2.2
|
||
|
|
||
|
/// @desc Converts gamma space color to linear space.
|
||
|
vec3 xGammaToLinear(vec3 rgb)
|
||
|
{
|
||
|
return pow(rgb, vec3(X_GAMMA));
|
||
|
}
|
||
|
|
||
|
/// @desc Converts linear space color to gamma space.
|
||
|
vec3 xLinearToGamma(vec3 rgb)
|
||
|
{
|
||
|
return pow(rgb, vec3(1.0 / X_GAMMA));
|
||
|
}
|
||
|
|
||
|
/// @desc Gets color's luminance.
|
||
|
float xLuminance(vec3 rgb)
|
||
|
{
|
||
|
return (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b);
|
||
|
}
|
||
|
/// @note Input color should be in gamma space.
|
||
|
/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html
|
||
|
vec4 xEncodeRGBM(vec3 color)
|
||
|
{
|
||
|
vec4 rgbm;
|
||
|
color *= 1.0 / 6.0;
|
||
|
rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 0.000001)), 0.0, 1.0);
|
||
|
rgbm.a = ceil(rgbm.a * 255.0) / 255.0;
|
||
|
rgbm.rgb = color / rgbm.a;
|
||
|
return rgbm;
|
||
|
}
|
||
|
|
||
|
/// @source https://graphicrants.blogspot.cz/2009/04/rgbm-color-encoding.html
|
||
|
vec3 xDecodeRGBM(vec4 rgbm)
|
||
|
{
|
||
|
return 6.0 * rgbm.rgb * rgbm.a;
|
||
|
}
|
||
|
|
||
|
/// @desc Unpacks material from textures.
|
||
|
/// @param texBaseOpacity RGB: base color, A: opacity
|
||
|
/// @param isRoughness
|
||
|
/// @param texNormalW
|
||
|
/// @param isMetallic
|
||
|
/// @param texMaterial
|
||
|
/// @param texSubsurface RGB: subsurface color, A: intensity
|
||
|
/// @param texEmissive RGBA: RGBM encoded emissive color
|
||
|
/// @param texLightmap RGBA: RGBM encoded lightmap
|
||
|
/// @param uvLightmap Lightmap texture coordinates
|
||
|
/// @param TBN Tangent-bitangent-normal matrix
|
||
|
/// @param uv Texture coordinates
|
||
|
Material UnpackMaterial(
|
||
|
sampler2D texBaseOpacity,
|
||
|
float isRoughness,
|
||
|
sampler2D texNormalW,
|
||
|
float isMetallic,
|
||
|
sampler2D texMaterial,
|
||
|
mat3 TBN,
|
||
|
vec2 uv)
|
||
|
{
|
||
|
Material m = CreateMaterial(TBN);
|
||
|
|
||
|
// Base color and opacity
|
||
|
vec4 baseOpacity = texture2D(texBaseOpacity,
|
||
|
uv
|
||
|
);
|
||
|
m.Base = xGammaToLinear(baseOpacity.rgb);
|
||
|
m.Opacity = baseOpacity.a;
|
||
|
|
||
|
// Normal vector and smoothness/roughness
|
||
|
vec4 normalW = texture2D(texNormalW,
|
||
|
uv
|
||
|
);
|
||
|
m.Normal = normalize(TBN * (normalW.rgb * 2.0 - 1.0));
|
||
|
|
||
|
if (isRoughness == 1.0)
|
||
|
{
|
||
|
m.Roughness = mix(0.1, 0.9, normalW.a);
|
||
|
m.Smoothness = 1.0 - m.Roughness;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m.Smoothness = mix(0.1, 0.9, normalW.a);
|
||
|
m.Roughness = 1.0 - m.Smoothness;
|
||
|
}
|
||
|
|
||
|
// Material properties
|
||
|
vec4 materialProps = texture2D(texMaterial,
|
||
|
uv
|
||
|
);
|
||
|
|
||
|
if (isMetallic == 1.0)
|
||
|
{
|
||
|
m.Metallic = materialProps.r;
|
||
|
m.AO = materialProps.g;
|
||
|
m.Specular = mix(F0_DEFAULT, m.Base, m.Metallic);
|
||
|
m.Base *= (1.0 - m.Metallic);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m.Specular = materialProps.rgb;
|
||
|
m.SpecularPower = exp2(1.0 + (m.Smoothness * 10.0));
|
||
|
}
|
||
|
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
#define X_PI 3.14159265359
|
||
|
#define X_2_PI 6.28318530718
|
||
|
|
||
|
/// @return x^2
|
||
|
#define xPow2(x) ((x) * (x))
|
||
|
|
||
|
/// @return x^3
|
||
|
#define xPow3(x) ((x) * (x) * (x))
|
||
|
|
||
|
/// @return x^4
|
||
|
#define xPow4(x) ((x) * (x) * (x) * (x))
|
||
|
|
||
|
/// @return x^5
|
||
|
#define xPow5(x) ((x) * (x) * (x) * (x) * (x))
|
||
|
|
||
|
/// @return arctan2(x,y)
|
||
|
#define xAtan2(x, y) atan(y, x)
|
||
|
|
||
|
/// @return Direction from point `from` to point `to` in degrees (0-360 range).
|
||
|
float xPointDirection(vec2 from, vec2 to)
|
||
|
{
|
||
|
float x = xAtan2(from.x - to.x, from.y - to.y);
|
||
|
return ((x > 0.0) ? x : (2.0 * X_PI + x)) * 180.0 / X_PI;
|
||
|
}
|
||
|
|
||
|
/// @desc Default specular color for dielectrics
|
||
|
/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||
|
#define X_F0_DEFAULT vec3(0.04, 0.04, 0.04)
|
||
|
|
||
|
/// @desc Normal distribution function
|
||
|
/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||
|
float xSpecularD_GGX(float roughness, float NdotH)
|
||
|
{
|
||
|
float r = xPow4(roughness);
|
||
|
float a = NdotH * NdotH * (r - 1.0) + 1.0;
|
||
|
return r / (X_PI * a * a);
|
||
|
}
|
||
|
|
||
|
/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
|
||
|
float xSpecularD_Approx(float roughness, float RdotL)
|
||
|
{
|
||
|
float a = roughness * roughness;
|
||
|
float a2 = a * a;
|
||
|
float rcp_a2 = 1.0 / a2;
|
||
|
// 0.5 / ln(2), 0.275 / ln(2)
|
||
|
float c = (0.72134752 * rcp_a2) + 0.39674113;
|
||
|
return (rcp_a2 * exp2((c * RdotL) - c));
|
||
|
}
|
||
|
|
||
|
/// @desc Roughness remapping for analytic lights.
|
||
|
/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||
|
float xK_Analytic(float roughness)
|
||
|
{
|
||
|
return xPow2(roughness + 1.0) * 0.125;
|
||
|
}
|
||
|
|
||
|
/// @desc Roughness remapping for IBL lights.
|
||
|
/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||
|
float xK_IBL(float roughness)
|
||
|
{
|
||
|
return xPow2(roughness) * 0.5;
|
||
|
}
|
||
|
|
||
|
/// @desc Geometric attenuation
|
||
|
/// @param k Use either xK_Analytic for analytic lights or xK_IBL for image based lighting.
|
||
|
/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||
|
float xSpecularG_Schlick(float k, float NdotL, float NdotV)
|
||
|
{
|
||
|
return (NdotL / (NdotL * (1.0 - k) + k))
|
||
|
* (NdotV / (NdotV * (1.0 - k) + k));
|
||
|
}
|
||
|
|
||
|
/// @desc Fresnel
|
||
|
/// @source https://en.wikipedia.org/wiki/Schlick%27s_approximation
|
||
|
vec3 xSpecularF_Schlick(vec3 f0, float VdotH)
|
||
|
{
|
||
|
return f0 + (1.0 - f0) * xPow5(1.0 - VdotH);
|
||
|
}
|
||
|
|
||
|
/// @desc Cook-Torrance microfacet specular shading
|
||
|
/// @note N = normalize(vertexNormal)
|
||
|
/// L = normalize(light - vertex)
|
||
|
/// V = normalize(camera - vertex)
|
||
|
/// H = normalize(L + V)
|
||
|
/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||
|
vec3 xBRDF(vec3 f0, float roughness, float NdotL, float NdotV, float NdotH, float VdotH)
|
||
|
{
|
||
|
vec3 specular = xSpecularD_GGX(roughness, NdotH)
|
||
|
* xSpecularF_Schlick(f0, VdotH)
|
||
|
* xSpecularG_Schlick(xK_Analytic(roughness), NdotL, NdotH);
|
||
|
return specular / ((4.0 * NdotL * NdotV) + 0.1);
|
||
|
}
|
||
|
|
||
|
vec3 SpecularGGX(Material m, vec3 N, vec3 V, vec3 L)
|
||
|
{
|
||
|
vec3 H = normalize(L + V);
|
||
|
float NdotL = max(dot(N, L), 0.0);
|
||
|
float NdotV = max(dot(N, V), 0.0);
|
||
|
float NdotH = max(dot(N, H), 0.0);
|
||
|
float VdotH = max(dot(V, H), 0.0);
|
||
|
return xBRDF(m.Specular, m.Roughness, NdotL, NdotV, NdotH, VdotH);
|
||
|
}
|
||
|
|
||
|
void DoDirectionalLightPS(
|
||
|
vec3 direction,
|
||
|
vec3 color,
|
||
|
float shadow,
|
||
|
vec3 vertex,
|
||
|
vec3 N,
|
||
|
vec3 V,
|
||
|
Material m,
|
||
|
inout vec3 diffuse,
|
||
|
inout vec3 specular,
|
||
|
inout vec3 subsurface)
|
||
|
{
|
||
|
vec3 L = normalize(-direction);
|
||
|
float NdotL = max(dot(N, L), 0.0);
|
||
|
color *= (1.0 - shadow) * NdotL;
|
||
|
diffuse += color;
|
||
|
specular += color * SpecularGGX(m, N, V, L);
|
||
|
}
|
||
|
|
||
|
void DoPointLightPS(
|
||
|
vec3 position,
|
||
|
float range,
|
||
|
vec3 color,
|
||
|
float shadow,
|
||
|
vec3 vertex,
|
||
|
vec3 N,
|
||
|
vec3 V,
|
||
|
Material m,
|
||
|
inout vec3 diffuse,
|
||
|
inout vec3 specular,
|
||
|
inout vec3 subsurface)
|
||
|
{
|
||
|
vec3 L = position - vertex;
|
||
|
float dist = length(L);
|
||
|
L = normalize(L);
|
||
|
float att = clamp(1.0 - (dist / range), 0.0, 1.0);
|
||
|
att *= att;
|
||
|
float NdotL = max(dot(N, L), 0.0);
|
||
|
color *= (1.0 - shadow) * NdotL * att;
|
||
|
diffuse += color;
|
||
|
specular += color * SpecularGGX(m, N, V, L);
|
||
|
}
|
||
|
|
||
|
void DoSpotLightPS(
|
||
|
vec3 position,
|
||
|
float range,
|
||
|
vec3 color,
|
||
|
float shadow,
|
||
|
vec3 direction,
|
||
|
float dcosInner,
|
||
|
float dcosOuter,
|
||
|
vec3 vertex,
|
||
|
vec3 N,
|
||
|
vec3 V,
|
||
|
Material m,
|
||
|
inout vec3 diffuse,
|
||
|
inout vec3 specular,
|
||
|
inout vec3 subsurface)
|
||
|
{
|
||
|
vec3 L = position - vertex;
|
||
|
float dist = length(L);
|
||
|
L = normalize(L);
|
||
|
float att = clamp(1.0 - (dist / range), 0.0, 1.0);
|
||
|
float theta = dot(L, normalize(-direction));
|
||
|
float epsilon = dcosInner - dcosOuter;
|
||
|
float intensity = clamp((theta - dcosOuter) / epsilon, 0.0, 1.0);
|
||
|
color *= (1.0 - shadow) * intensity * att;
|
||
|
diffuse += color;
|
||
|
specular += color * SpecularGGX(m, N, V, L);
|
||
|
}
|
||
|
void Exposure()
|
||
|
{
|
||
|
gl_FragColor.rgb = vec3(1.0) - exp(-gl_FragColor.rgb * bbmod_Exposure);
|
||
|
}
|
||
|
void Fog(float depth)
|
||
|
{
|
||
|
vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a;
|
||
|
vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a;
|
||
|
vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a;
|
||
|
vec3 fogColor = xGammaToLinear(bbmod_FogColor.rgb) * (ambientUp + ambientDown + directionalLightColor);
|
||
|
float fogStrength = clamp((depth - bbmod_FogStart) * bbmod_FogRcpRange, 0.0, 1.0) * bbmod_FogColor.a;
|
||
|
gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, fogStrength * bbmod_FogIntensity);
|
||
|
}
|
||
|
|
||
|
void GammaCorrect()
|
||
|
{
|
||
|
gl_FragColor.rgb = xLinearToGamma(gl_FragColor.rgb);
|
||
|
}
|
||
|
// Source: https://gamedev.stackexchange.com/questions/169508/octahedral-impostors-octahedral-mapping
|
||
|
|
||
|
/// @param dir Sampling dir vector in world-space.
|
||
|
/// @return UV coordinates on an octahedron map.
|
||
|
vec2 xVec3ToOctahedronUv(vec3 dir)
|
||
|
{
|
||
|
vec3 octant = sign(dir);
|
||
|
float sum = dot(dir, octant);
|
||
|
vec3 octahedron = dir / sum;
|
||
|
if (octahedron.z < 0.0)
|
||
|
{
|
||
|
vec3 absolute = abs(octahedron);
|
||
|
octahedron.xy = octant.xy * vec2(1.0 - absolute.y, 1.0 - absolute.x);
|
||
|
}
|
||
|
return octahedron.xy * 0.5 + 0.5;
|
||
|
}
|
||
|
|
||
|
/// @desc Converts octahedron UV into a world-space vector.
|
||
|
vec3 xOctahedronUvToVec3Normalized(vec2 uv)
|
||
|
{
|
||
|
vec3 position = vec3(2.0 * (uv - 0.5), 0);
|
||
|
vec2 absolute = abs(position.xy);
|
||
|
position.z = 1.0 - absolute.x - absolute.y;
|
||
|
if (position.z < 0.0)
|
||
|
{
|
||
|
position.xy = sign(position.xy) * vec2(1.0 - absolute.y, 1.0 - absolute.x);
|
||
|
}
|
||
|
return position;
|
||
|
}
|
||
|
|
||
|
vec3 xDiffuseIBL(sampler2D ibl, vec2 texel, vec3 N)
|
||
|
{
|
||
|
const float s = 1.0 / 8.0;
|
||
|
const float r2 = 7.0;
|
||
|
|
||
|
vec2 uv0 = xVec3ToOctahedronUv(N);
|
||
|
uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s;
|
||
|
uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y);
|
||
|
|
||
|
return xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0)));
|
||
|
}
|
||
|
|
||
|
/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
|
||
|
vec2 xEnvBRDFApprox(float roughness, float NdotV)
|
||
|
{
|
||
|
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
|
||
|
const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
|
||
|
vec4 r = (roughness * c0) + c1;
|
||
|
float a004 = (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y;
|
||
|
return ((vec2(-1.04, 1.04) * a004) + r.zw);
|
||
|
}
|
||
|
|
||
|
/// @source https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
|
||
|
float xEnvBRDFApproxNonmetal(float roughness, float NdotV)
|
||
|
{
|
||
|
// Same as EnvBRDFApprox(0.04, Roughness, NdotV)
|
||
|
const vec2 c0 = vec2(-1.0, -0.0275);
|
||
|
const vec2 c1 = vec2(1.0, 0.0425);
|
||
|
vec2 r = (roughness * c0) + c1;
|
||
|
return (min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x) + r.y;
|
||
|
}
|
||
|
|
||
|
// Fully rough optimization:
|
||
|
// xEnvBRDFApprox(SpecularColor, 1, 1) == SpecularColor * 0.4524 - 0.0024
|
||
|
// DiffuseColor += SpecularColor * 0.45;
|
||
|
// SpecularColor = 0.0;
|
||
|
|
||
|
/// @source http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||
|
vec3 xSpecularIBL(sampler2D ibl, vec2 texel/*, sampler2D brdf*/, vec3 f0, float roughness, vec3 N, vec3 V)
|
||
|
{
|
||
|
float NdotV = clamp(dot(N, V), 0.0, 1.0);
|
||
|
vec3 R = 2.0 * dot(V, N) * N - V;
|
||
|
// vec2 envBRDF = texture2D(brdf, vec2(roughness, NdotV)).xy;
|
||
|
vec2 envBRDF = xEnvBRDFApprox(roughness, NdotV);
|
||
|
|
||
|
const float s = 1.0 / 8.0;
|
||
|
float r = roughness * 7.0;
|
||
|
float r2 = floor(r);
|
||
|
float rDiff = r - r2;
|
||
|
|
||
|
vec2 uv0 = xVec3ToOctahedronUv(R);
|
||
|
uv0.x = (r2 + mix(texel.x, 1.0 - texel.x, uv0.x)) * s;
|
||
|
uv0.y = mix(texel.y, 1.0 - texel.y, uv0.y);
|
||
|
|
||
|
vec2 uv1 = uv0;
|
||
|
uv1.x = uv1.x + s;
|
||
|
|
||
|
vec3 specular = f0 * envBRDF.x + envBRDF.y;
|
||
|
|
||
|
vec3 col0 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv0))) * specular;
|
||
|
vec3 col1 = xGammaToLinear(xDecodeRGBM(texture2D(ibl, uv1))) * specular;
|
||
|
|
||
|
return mix(col0, col1, rDiff);
|
||
|
}
|
||
|
/// @param tanAspect (tanFovY*(screenWidth/screenHeight),-tanFovY), where
|
||
|
/// tanFovY = dtan(fov*0.5)
|
||
|
/// @param texCoord Sceen-space UV.
|
||
|
/// @param depth Scene depth at texCoord.
|
||
|
/// @return Point projected to view-space.
|
||
|
vec3 xProject(vec2 tanAspect, vec2 texCoord, float depth)
|
||
|
{
|
||
|
return vec3(tanAspect * (texCoord * 2.0 - 1.0) * depth, depth);
|
||
|
}
|
||
|
|
||
|
/// @param p A point in clip space (transformed by projection matrix, but not
|
||
|
/// normalized).
|
||
|
/// @return P's UV coordinates on the screen.
|
||
|
vec2 xUnproject(vec4 p)
|
||
|
{
|
||
|
vec2 uv = p.xy / p.w;
|
||
|
uv = uv * 0.5 + 0.5;
|
||
|
#if defined(_YY_HLSL11_) || defined(_YY_PSSL_)
|
||
|
uv.y = 1.0 - uv.y;
|
||
|
#endif
|
||
|
return uv;
|
||
|
}
|
||
|
/// @param d Linearized depth to encode.
|
||
|
/// @return Encoded depth.
|
||
|
/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
|
||
|
vec3 xEncodeDepth(float d)
|
||
|
{
|
||
|
const float inv255 = 1.0 / 255.0;
|
||
|
vec3 enc;
|
||
|
enc.x = d;
|
||
|
enc.y = d * 255.0;
|
||
|
enc.z = enc.y * 255.0;
|
||
|
enc = fract(enc);
|
||
|
float temp = enc.z * inv255;
|
||
|
enc.x -= enc.y * inv255;
|
||
|
enc.y -= temp;
|
||
|
enc.z -= temp;
|
||
|
return enc;
|
||
|
}
|
||
|
|
||
|
/// @param c Encoded depth.
|
||
|
/// @return Docoded linear depth.
|
||
|
/// @source http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
|
||
|
float xDecodeDepth(vec3 c)
|
||
|
{
|
||
|
const float inv255 = 1.0 / 255.0;
|
||
|
return c.x + (c.y * inv255) + (c.z * inv255 * inv255);
|
||
|
}
|
||
|
// Shadowmap filtering source: https://www.gamedev.net/tutorials/programming/graphics/contact-hardening-soft-shadows-made-fast-r4906/
|
||
|
float InterleavedGradientNoise(vec2 positionScreen)
|
||
|
{
|
||
|
vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189);
|
||
|
return fract(magic.z * fract(dot(positionScreen, magic.xy)));
|
||
|
}
|
||
|
vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi)
|
||
|
{
|
||
|
float GoldenAngle = 2.4;
|
||
|
float r = sqrt(float(sampleIndex) + 0.5) / sqrt(float(samplesCount));
|
||
|
float theta = float(sampleIndex) * GoldenAngle + phi;
|
||
|
float sine = sin(theta);
|
||
|
float cosine = cos(theta);
|
||
|
return vec2(r * cosine, r * sine);
|
||
|
}
|
||
|
|
||
|
float ShadowMap(sampler2D shadowMap, vec2 texel, vec2 uv, float compareZ)
|
||
|
{
|
||
|
if (clamp(uv.xy, vec2(0.0), vec2(1.0)) != uv.xy)
|
||
|
{
|
||
|
return 0.0;
|
||
|
}
|
||
|
float shadow = 0.0;
|
||
|
float noise = 6.28 * InterleavedGradientNoise(gl_FragCoord.xy);
|
||
|
float bias = bbmod_ShadowmapBias / bbmod_ShadowmapArea;
|
||
|
for (int i = 0; i < SHADOWMAP_SAMPLE_COUNT; ++i)
|
||
|
{
|
||
|
vec2 uv2 = uv + VogelDiskSample(i, SHADOWMAP_SAMPLE_COUNT, noise) * texel * 4.0;
|
||
|
float depth = xDecodeDepth(texture2D(shadowMap, uv2).rgb);
|
||
|
if (bias != 0.0)
|
||
|
{
|
||
|
shadow += clamp((compareZ - depth) / bias, 0.0, 1.0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
shadow += step(depth, compareZ);
|
||
|
}
|
||
|
}
|
||
|
return (shadow / float(SHADOWMAP_SAMPLE_COUNT));
|
||
|
}
|
||
|
|
||
|
void PBRShader(Material material, float depth)
|
||
|
{
|
||
|
vec3 N = material.Normal;
|
||
|
vec3 V = (v_vEye.w == 1.0) ? v_vEye.xyz : normalize(bbmod_CamPos - v_vVertex);
|
||
|
vec3 lightDiffuse = vec3(0.0);
|
||
|
vec3 lightSpecular = vec3(0.0);
|
||
|
vec3 lightSubsurface = vec3(0.0);
|
||
|
|
||
|
// Ambient light
|
||
|
vec3 ambientUp = xGammaToLinear(bbmod_LightAmbientUp.rgb) * bbmod_LightAmbientUp.a;
|
||
|
vec3 ambientDown = xGammaToLinear(bbmod_LightAmbientDown.rgb) * bbmod_LightAmbientDown.a;
|
||
|
lightDiffuse += mix(ambientDown, ambientUp, N.z * 0.5 + 0.5);
|
||
|
|
||
|
// Shadow mapping
|
||
|
float shadow = 0.0;
|
||
|
if (bbmod_ShadowmapEnablePS == 1.0)
|
||
|
{
|
||
|
vec4 shadowmapPos = v_vPosShadowmap;
|
||
|
shadowmapPos.xy /= shadowmapPos.w;
|
||
|
float shadowmapAtt = (bbmod_ShadowCasterIndex == -1.0)
|
||
|
? clamp((1.0 - length(shadowmapPos.xy)) / 0.1, 0.0, 1.0)
|
||
|
: 1.0;
|
||
|
shadowmapPos.xy = shadowmapPos.xy * 0.5 + 0.5;
|
||
|
#if defined(_YY_HLSL11_) || defined(_YY_PSSL_)
|
||
|
shadowmapPos.y = 1.0 - shadowmapPos.y;
|
||
|
#endif
|
||
|
shadowmapPos.z /= bbmod_ShadowmapArea;
|
||
|
|
||
|
shadow = ShadowMap(bbmod_Shadowmap, bbmod_ShadowmapTexel, shadowmapPos.xy, shadowmapPos.z)
|
||
|
* shadowmapAtt;
|
||
|
}
|
||
|
|
||
|
// IBL
|
||
|
lightDiffuse += xDiffuseIBL(bbmod_IBL, bbmod_IBLTexel, N);
|
||
|
lightSpecular += xSpecularIBL(bbmod_IBL, bbmod_IBLTexel, material.Specular, material.Roughness, N, V);
|
||
|
// TODO: Subsurface scattering for IBL
|
||
|
|
||
|
// Directional light
|
||
|
vec3 directionalLightColor = xGammaToLinear(bbmod_LightDirectionalColor.rgb) * bbmod_LightDirectionalColor.a;
|
||
|
DoDirectionalLightPS(
|
||
|
bbmod_LightDirectionalDir,
|
||
|
directionalLightColor,
|
||
|
(bbmod_ShadowCasterIndex == -1.0) ? shadow : 0.0,
|
||
|
v_vVertex, N, V, material, lightDiffuse, lightSpecular, lightSubsurface);
|
||
|
|
||
|
// SSAO
|
||
|
float ssao = texture2D(bbmod_SSAO, xUnproject(v_vPosition)).r;
|
||
|
lightDiffuse *= ssao;
|
||
|
lightSpecular *= ssao;
|
||
|
|
||
|
// Punctual lights
|
||
|
for (int i = 0; i < MAX_PUNCTUAL_LIGHTS; ++i)
|
||
|
{
|
||
|
vec4 positionRange = bbmod_LightPunctualDataA[i * 2];
|
||
|
vec4 colorAlpha = bbmod_LightPunctualDataA[(i * 2) + 1];
|
||
|
vec3 isSpotInnerOuter = bbmod_LightPunctualDataB[i * 2];
|
||
|
vec3 direction = bbmod_LightPunctualDataB[(i * 2) + 1];
|
||
|
vec3 color = xGammaToLinear(colorAlpha.rgb) * colorAlpha.a;
|
||
|
|
||
|
if (isSpotInnerOuter.x == 1.0)
|
||
|
{
|
||
|
DoSpotLightPS(
|
||
|
positionRange.xyz, positionRange.w, color,
|
||
|
(bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0,
|
||
|
direction, isSpotInnerOuter.y, isSpotInnerOuter.z,
|
||
|
v_vVertex, N, V, material,
|
||
|
lightDiffuse, lightSpecular, lightSubsurface);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DoPointLightPS(
|
||
|
positionRange.xyz, positionRange.w, color,
|
||
|
(bbmod_ShadowCasterIndex == float(i)) ? shadow : 0.0,
|
||
|
v_vVertex, N, V, material,
|
||
|
lightDiffuse, lightSpecular, lightSubsurface);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Lightmap
|
||
|
|
||
|
// Diffuse
|
||
|
gl_FragColor.rgb = material.Base * lightDiffuse;
|
||
|
// Specular
|
||
|
gl_FragColor.rgb += lightSpecular;
|
||
|
// Ambient occlusion
|
||
|
gl_FragColor.rgb *= material.AO;
|
||
|
// Emissive
|
||
|
gl_FragColor.rgb += material.Emissive;
|
||
|
// Subsurface scattering
|
||
|
gl_FragColor.rgb += lightSubsurface;
|
||
|
// Opacity
|
||
|
gl_FragColor.a = material.Opacity;
|
||
|
// Soft particles
|
||
|
// Fog
|
||
|
Fog(depth);
|
||
|
|
||
|
Exposure();
|
||
|
GammaCorrect();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Main
|
||
|
//
|
||
|
void main()
|
||
|
{
|
||
|
Material material = UnpackMaterial(
|
||
|
bbmod_BaseOpacity,
|
||
|
bbmod_IsRoughness,
|
||
|
bbmod_NormalW,
|
||
|
bbmod_IsMetallic,
|
||
|
bbmod_Material,
|
||
|
v_mTBN,
|
||
|
v_vTexCoord);
|
||
|
|
||
|
// Splatmap
|
||
|
vec4 splatmap = texture2D(bbmod_Splatmap, v_vSplatmapCoord);
|
||
|
if (bbmod_SplatmapIndex >= 0)
|
||
|
{
|
||
|
// splatmap[bbmod_SplatmapIndex] does not work in HTML5
|
||
|
material.Opacity *= ((bbmod_SplatmapIndex == 0) ? splatmap.r
|
||
|
: ((bbmod_SplatmapIndex == 1) ? splatmap.g
|
||
|
: ((bbmod_SplatmapIndex == 2) ? splatmap.b
|
||
|
: splatmap.a)));
|
||
|
}
|
||
|
|
||
|
material.Base *= bbmod_BaseOpacityMultiplier.rgb;
|
||
|
material.Opacity *= bbmod_BaseOpacityMultiplier.a;
|
||
|
|
||
|
if (material.Opacity < bbmod_AlphaTest)
|
||
|
{
|
||
|
discard;
|
||
|
}
|
||
|
|
||
|
PBRShader(material, v_vPosition.z);
|
||
|
|
||
|
}
|