722 lines
22 KiB
GLSL
722 lines
22 KiB
GLSL
//This shader file is part of FLAR - Advanced Renderer for Freelancer by Schmackbolzen
|
|
//If you use the supplied shader files you may not modify them unless you state in them what you changed
|
|
//and also mention the source or who the author is.
|
|
//PBR lighting shader for FLAR by Schmackbolzen
|
|
//Sources are either mentioned in the comments below,
|
|
//from https://learnopengl.com/PBR/Lighting or https://learnopengl.com/PBR/IBL/Specular-IBL
|
|
//or "Parallel-Split Shadow Maps on Programmable GPUs" GPU Gems 3 (2005)
|
|
|
|
#version 330
|
|
#include "ColorConversion.inc"
|
|
#define MAX_SHADOWS $MAX_SHADOWS
|
|
#define NUM_SPLITS $NUM_SPLITS
|
|
#define USE_PCF $USE_PCF
|
|
#define USE_HQ_DIFFUSE $USE_HQ_DIFFUSE
|
|
#define USE_PARALLAX_MAPS $USE_PARALLAX_MAPS
|
|
#define USE_CLUSTERED_SHADING $USE_CLUSTERED_SHADING
|
|
|
|
|
|
//#define SIMPLE_KD
|
|
#define IBL_MULTIPLE_SCATTERING
|
|
|
|
#if USE_CLUSTERED_SHADING == 1
|
|
#define NUM_POSSIBLE_LIGHTS $NUM_POSSIBLE_LIGHTS
|
|
#define LIGHT_GRID_TILE_DIM_X $LIGHT_GRID_TILE_DIM_X
|
|
#define LIGHT_GRID_TILE_DIM_Y $LIGHT_GRID_TILE_DIM_Y
|
|
#define LIGHT_GRID_MAX_DIM_X $LIGHT_GRID_MAX_DIM_X
|
|
#define LIGHT_GRID_MAX_DIM_Y $LIGHT_GRID_MAX_DIM_Y
|
|
#endif
|
|
|
|
const float PBR_FACTOR = 2;
|
|
const float PBR_FACTOR_ENV = 1.5;
|
|
|
|
in vec3 eyeVec;
|
|
in vec3 v,vWorld,worldNormal;
|
|
in vec3 N;
|
|
in mat3 tbnMatrix,tbnMatrixObj;
|
|
in vec4 shadowCoord[MAX_SHADOWS*NUM_SPLITS];
|
|
in vec4 texCoords;
|
|
in mat3 worldRotMat;
|
|
in float alphaValue;
|
|
|
|
in float fogEnd;
|
|
in float fogScale;
|
|
|
|
struct Light{
|
|
vec3 ambient;
|
|
vec3 diffuse;
|
|
vec4 position;
|
|
vec3 spotDirection;
|
|
float spotExponent;
|
|
float spotCosPhi;
|
|
float spotCosTheta;
|
|
float constantAttenuation;
|
|
float linearAttenuation;
|
|
float quadraticAttenuation;
|
|
};
|
|
|
|
struct glFogParameters
|
|
{
|
|
vec3 color;
|
|
float density;
|
|
float start;
|
|
float end;
|
|
};
|
|
|
|
#if USE_CLUSTERED_SHADING == 1
|
|
//Has to be 16 byte aligned (otherwise it will be padded)
|
|
struct ClusteredLight{
|
|
vec4 position;
|
|
vec4 diffuse;
|
|
};
|
|
|
|
layout (std140) uniform LightSourceClustered
|
|
{
|
|
ClusteredLight clusteredLights[NUM_POSSIBLE_LIGHTS];
|
|
};
|
|
|
|
uniform isamplerBuffer clusterLightIndexListsTex;
|
|
uniform usamplerBuffer clusterGridTex;
|
|
#endif
|
|
|
|
uniform glFogParameters glFog;
|
|
uniform Light glLightSource[8];
|
|
uniform sampler2D tex;
|
|
uniform sampler2D tex2;
|
|
uniform sampler2D heightMap;
|
|
uniform sampler2D normalMap;
|
|
uniform sampler2D roughnessMap;
|
|
uniform sampler2D metalnessMap;
|
|
uniform sampler2D reflectionMap;
|
|
uniform sampler2D brdfLUT;
|
|
uniform sampler2DArrayShadow shadowMap[MAX_SHADOWS];
|
|
uniform float splitPlanes[NUM_SPLITS];
|
|
|
|
uniform samplerCube envMap;
|
|
uniform samplerCube irradianceMap;
|
|
uniform vec2 texture1Size;
|
|
uniform vec2 textureShadowSize;
|
|
uniform int lightCount;
|
|
uniform int shadowCount;
|
|
uniform int tex2Mode;
|
|
uniform int fogMode;
|
|
uniform float metalnessConst;
|
|
uniform float roughnessConst;
|
|
uniform float roughnessTextureBias;
|
|
|
|
uniform mat4 invWorldView;
|
|
uniform vec3 camPos;
|
|
uniform vec3 ambientColor;
|
|
uniform vec3 matAmbientColor;
|
|
uniform vec3 matDiffuseColor;
|
|
uniform vec3 matEmissiveColor;
|
|
uniform int alphaTestCompareMethod;
|
|
uniform float alphaTestValue;
|
|
|
|
uniform bool enableNormalMaps;
|
|
uniform bool enableParallaxMaps;
|
|
uniform bool enableFirstTexture;
|
|
uniform bool enableSecondTexture;
|
|
uniform bool enableFog;
|
|
uniform bool enableEnvMap;
|
|
uniform bool enableTex2Modulate2X;
|
|
uniform bool enableRoughnessMap;
|
|
uniform bool enableMetalnessMap;
|
|
uniform bool enableAlphaTest;
|
|
#if USE_CLUSTERED_SHADING == 1
|
|
uniform bool enableClusteredShading;
|
|
uniform float recNear; /**< 1.0f / g_near */
|
|
uniform float recLogSD1; /**< 1.0f / logf(sD + 1.0f), used to compute cluster index. */
|
|
#endif
|
|
|
|
|
|
|
|
float HEIGHT_SCALE = 0.01;
|
|
|
|
const float PI = 3.141592654;
|
|
|
|
out vec4 diffuseColorOut;
|
|
|
|
|
|
float ShadowCalculation(const float bias, const int shadowMapIndex, const int lightIndex, const sampler2DArrayShadow shadowSampler)
|
|
{
|
|
vec4 fragPosLightSpace = shadowCoord[lightIndex*NUM_SPLITS+shadowMapIndex];
|
|
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
|
#if USE_PCF == 1
|
|
float currentDepth = clamp(projCoords.z,0,1);
|
|
|
|
|
|
// Check whether current frag pos is in shadow
|
|
//if(projCoords.z <= 1.0)
|
|
{
|
|
float shadow = 0.0;
|
|
vec2 texelSize = 1.0 / vec2(textureShadowSize);
|
|
for(int x = -1; x <= 1; ++x)
|
|
{
|
|
for(int y = -1; y <= 1; ++y)
|
|
{
|
|
float pcfDepth = clamp(texture(shadowSampler, vec4(projCoords.xy + vec2(x, y) * texelSize, shadowMapIndex, projCoords.z - bias)),0,1);
|
|
shadow += pcfDepth;
|
|
}
|
|
}
|
|
shadow *= 1.0/9.0;
|
|
return shadow;
|
|
}
|
|
//else
|
|
//return 1;
|
|
#else
|
|
return texture(shadowSampler, vec4(projCoords.xy, shadowMapIndex, projCoords.z - bias));
|
|
#endif
|
|
}
|
|
|
|
float ShadowCalculationForAllSplitPlanes(const int lightIndex, const vec3 lightDirViewSpace, const vec3 normal, float lightIntensity, const sampler2DArrayShadow shadowSampler)
|
|
{
|
|
float distance = v.z;
|
|
float bias = max(0.001 * (1.0 - dot(normal, lightDirViewSpace)), 0.0003);
|
|
|
|
bool applied = false;
|
|
|
|
for (int i = 0; i < NUM_SPLITS; i++)
|
|
if(!applied && (distance < splitPlanes[i]))
|
|
{
|
|
//bias = ((i+1)*0.1)*0.001;
|
|
lightIntensity *= ShadowCalculation(bias, i, lightIndex, shadowSampler);
|
|
applied=true;
|
|
//Graphics cards hate break (SLOW), hence boolean value above
|
|
//break;
|
|
}
|
|
return lightIntensity;
|
|
|
|
}
|
|
|
|
//Parallax mapping with offset limiting
|
|
//You can try different parallax mapping techniques, but they all have problems with steep angles so I decided this is something for later
|
|
vec2 ParallaxMapping(vec3 V, vec2 T)
|
|
{
|
|
// get depth for this fragment
|
|
float initialHeight = texture2D(heightMap, T).r-0.5;
|
|
|
|
// calculate amount of offset for Parallax Mapping With Offset Limiting
|
|
vec2 texCoordOffset = HEIGHT_SCALE * V.xy * initialHeight;
|
|
|
|
// return modified texture coordinates
|
|
return T + texCoordOffset;
|
|
}
|
|
|
|
float DistributionGGX(float NdotH, float roughnessSquared)
|
|
{
|
|
float a = roughnessSquared;
|
|
float a2 = a*a;
|
|
float NdotH2 = NdotH*NdotH;
|
|
|
|
float nom = a2;
|
|
float denom = NdotH2 * (a2 - 1.0) + 1.0;
|
|
denom = PI * denom * denom + 0.0001;
|
|
|
|
return nom / denom;
|
|
}
|
|
|
|
//"PBR Diffuse Lighting for GGX+Smith Microsurfaces", Earl Hammon, Jr.
|
|
float GeometrySmithG2HammonDenom(float NdotL, float NdotV, float roughnessSquared)
|
|
{
|
|
float denom = mix(2*NdotL*NdotV,NdotL+NdotV,roughnessSquared);
|
|
return denom;
|
|
}
|
|
|
|
vec3 fresnelSchlick(float cosTheta, vec3 F0)
|
|
{
|
|
return F0 + (1.0 - F0) * pow(max(1.0 - cosTheta,0), 5.0);
|
|
}
|
|
|
|
|
|
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
|
|
{
|
|
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(max(1.0 - cosTheta, 0.0), 5.0);
|
|
}
|
|
|
|
struct LightValues
|
|
{
|
|
vec3 lightVec;
|
|
vec3 lightDir;
|
|
vec3 lightDirViewSpace;
|
|
float attenFactor;
|
|
float lightDist;
|
|
};
|
|
|
|
LightValues CalculateLightValues(const int index)
|
|
{
|
|
LightValues result;
|
|
if(glLightSource[index].spotCosTheta != -1.)
|
|
{
|
|
result.lightVec = glLightSource[index].position.xyz - v;
|
|
result.lightDirViewSpace=normalize(result.lightVec);
|
|
result.lightDist = length(result.lightVec);
|
|
result.lightDir = normalize(result.lightVec);
|
|
|
|
float angle = max(dot(normalize(glLightSource[index].spotDirection), -result.lightDir),0);
|
|
|
|
if (angle > glLightSource[index].spotCosPhi)
|
|
{
|
|
float spotAtten;
|
|
if (angle < glLightSource[index].spotCosTheta)
|
|
spotAtten = (angle-glLightSource[index].spotCosPhi)/(glLightSource[index].spotCosTheta-glLightSource[index].spotCosPhi);
|
|
else
|
|
spotAtten = 1;
|
|
|
|
|
|
if(glLightSource[index].spotExponent != 1.0)
|
|
spotAtten = pow(spotAtten, glLightSource[index].spotExponent);
|
|
|
|
result.attenFactor = spotAtten * min(1.0/ (glLightSource[index].constantAttenuation +
|
|
glLightSource[index].linearAttenuation * result.lightDist +
|
|
glLightSource[index].quadraticAttenuation * result.lightDist * result.lightDist),1);
|
|
}
|
|
else
|
|
result.attenFactor = 0.;
|
|
}
|
|
else if (glLightSource[index].position.w != 0.0)
|
|
{
|
|
result.lightVec = glLightSource[index].position.xyz - v;
|
|
result.lightDirViewSpace=normalize(result.lightVec);
|
|
result.lightDist = length(result.lightVec);
|
|
|
|
if (enableNormalMaps)
|
|
result.lightVec = tbnMatrix*result.lightVec;
|
|
result.lightDir = normalize(result.lightVec);
|
|
|
|
|
|
// positional light source
|
|
result.attenFactor = clamp(1.0/( glLightSource[index].constantAttenuation +
|
|
glLightSource[index].linearAttenuation * result.lightDist +
|
|
glLightSource[index].quadraticAttenuation * result.lightDist * result.lightDist ),0,1);
|
|
}
|
|
else
|
|
{
|
|
// directional light source
|
|
result.attenFactor = 1.0;
|
|
|
|
if (enableNormalMaps)
|
|
{
|
|
result.lightDirViewSpace=normalize(glLightSource[index].position.xyz);
|
|
result.lightVec = tbnMatrix*glLightSource[index].position.xyz;
|
|
result.lightDir = normalize(result.lightVec);
|
|
}
|
|
else
|
|
{
|
|
result.lightVec = glLightSource[index].position.xyz;
|
|
result.lightDir = normalize(result.lightVec);
|
|
result.lightDirViewSpace = result.lightDir;
|
|
}
|
|
result.lightDist = length(result.lightVec);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#if USE_CLUSTERED_SHADING == 1
|
|
LightValues CalculateLightValuesClustered(const int index)
|
|
{
|
|
LightValues result;
|
|
|
|
result.lightVec = clusteredLights[index].position.xyz - v;
|
|
result.lightDirViewSpace=normalize(result.lightVec);
|
|
result.lightDist = length(result.lightVec);
|
|
|
|
if (enableNormalMaps)
|
|
result.lightVec = tbnMatrix*result.lightVec;
|
|
result.lightDir = normalize(result.lightVec);
|
|
|
|
// positional light source
|
|
float inner = 0.0;
|
|
float att = max(1.0 - max(0.0, (result.lightDist - inner) / (clusteredLights[index].position.w - inner)), 0.0);
|
|
result.attenFactor = att;
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
vec3 ApplyAmbientLight(const int index,const vec3 firstTexture, const float intensity, vec3 lightSum)
|
|
{
|
|
vec3 IAmbient = (matAmbientColor)*(PBR_FACTOR*glLightSource[index].ambient)*firstTexture;
|
|
lightSum.rgb += IAmbient*intensity;
|
|
return lightSum;
|
|
}
|
|
|
|
vec3 ApplyPBRLight(vec3 lightDiffuseColor, const LightValues lightValues, const vec3 mapNormal, const vec3 eyeV, const vec3 F0, const float roughness, const vec3 IDiffuse, const vec3 firstTexture, vec3 lightSum, const float metalness)
|
|
{
|
|
|
|
float visible = 1.0;
|
|
float intensity = lightValues.attenFactor;
|
|
|
|
//vec3 IAmbient = (matAmbientColor.rgb)*(PBR_FACTOR*glLightSource[index].ambient)*firstTexture;
|
|
//lightSum.rgb += IAmbient*intensity;
|
|
|
|
vec3 L = lightValues.lightDir;
|
|
vec3 V = normalize(eyeV);
|
|
vec3 H = normalize(L + V);
|
|
|
|
vec3 reflected;
|
|
vec3 N = mapNormal;
|
|
|
|
float HdotV = max(dot(H, V), 0.0);
|
|
float NdotL = max(dot(N, L), 0.0);
|
|
float NdotV = max(dot(N, V), 0.0001);
|
|
float NdotH = max(dot(N, H), 0.0001);
|
|
|
|
vec3 F = fresnelSchlick(HdotV, F0);
|
|
|
|
// Cook-Torrance BRDF
|
|
float roughnessSquared = roughness*roughness;
|
|
float NDF = DistributionGGX(NdotH, roughnessSquared);
|
|
|
|
vec3 nominator = NDF * F;
|
|
float denominator = 2.0 * GeometrySmithG2HammonDenom(NdotV, NdotL, roughnessSquared) + 0.00001; // 0.00001 to prevent divide by zero.
|
|
vec3 specular = nominator/denominator;
|
|
|
|
#ifndef SIMPLE_KD
|
|
vec3 kD=mix(vec3(1.0)-F,vec3(0.0),metalness);
|
|
#else
|
|
float kD = 1.0 - metalness;
|
|
#endif
|
|
|
|
#if USE_HQ_DIFFUSE == 1
|
|
//GGX Diffuse Approximation from
|
|
//"PBR Diffuse Lighting for GGX+Smith Microsurfaces, Earl Hammon, Jr."
|
|
float LdotV = dot(L, V);
|
|
float facing = 0.5+0.5*LdotV;
|
|
float rough = facing*(0.9-0.4*facing)*(.5/(NdotH+0.05) + 1); //+0.05 to prevent too large values
|
|
float smooth_ = 1.05*(1-pow(1-NdotL,5))*(1-pow(1-NdotV,5));
|
|
float single = 1./PI*mix(smooth_, rough, roughnessSquared);
|
|
float multi = 0.1159*roughnessSquared;
|
|
vec3 diffuse = kD*IDiffuse.rgb*(single+IDiffuse.rgb*multi);
|
|
#else
|
|
vec3 diffuse = kD*IDiffuse.rgb/PI;
|
|
#endif
|
|
|
|
lightSum+= PBR_FACTOR*lightDiffuseColor*(diffuse + specular)*intensity*NdotL;
|
|
|
|
|
|
|
|
return lightSum;
|
|
}
|
|
|
|
vec3 ApplyLight(const int index, const LightValues lightValues, const vec3 mapNormal, const vec3 eyeV, const vec3 F0, const float roughness, const vec3 IDiffuse, const vec3 firstTexture, vec3 lightSum, const float metalness)
|
|
{
|
|
lightSum=ApplyAmbientLight(index, firstTexture, lightValues.attenFactor, lightSum);
|
|
lightSum=ApplyPBRLight(glLightSource[index].diffuse.rgb, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse, firstTexture, lightSum, metalness);
|
|
return lightSum;
|
|
}
|
|
|
|
#if USE_CLUSTERED_SHADING == 1
|
|
//Clustered shading code partially from
|
|
//"Demo code implementing Clustered Forward Shading, accompanying the talk
|
|
//'Tiled and Clustered Forward Shading' presented at Siggraph 2012."
|
|
|
|
/**
|
|
* Computes the {i,j,k} integer index into the cluster grid. For details see our paper:
|
|
* 'Clustered Deferred and Forward Shading'
|
|
* http://www.cse.chalmers.se/~olaolss/main_frame.php?contents=publication&id=clustered_shading
|
|
*/
|
|
ivec3 calcClusterLoc(vec2 fragPos, float viewSpaceZ)
|
|
{
|
|
// i and j coordinates are just the same as tiled shading, and based on screen space position.
|
|
ivec2 l = ivec2(int(fragPos.x) / LIGHT_GRID_TILE_DIM_X, int(fragPos.y) / LIGHT_GRID_TILE_DIM_Y);
|
|
|
|
// k is based on the log of the view space Z coordinate.
|
|
float gridLocZ = log(viewSpaceZ * recNear) * recLogSD1;
|
|
|
|
return ivec3(l, int(gridLocZ));
|
|
}
|
|
|
|
/**
|
|
* Linearlizes the 3D cluster location into an offset, which can be used as an
|
|
* address into a linear buffer.
|
|
*/
|
|
int calcClusterOffset(ivec3 clusterLoc)
|
|
{
|
|
return (clusterLoc.z * LIGHT_GRID_MAX_DIM_Y + clusterLoc.y) * LIGHT_GRID_MAX_DIM_X + clusterLoc.x;
|
|
}
|
|
|
|
/**
|
|
* Convenience shorthand function, see above...
|
|
*/
|
|
int calcClusterOffset(vec2 fragPos, float viewSpaceZ)
|
|
{
|
|
return calcClusterOffset(calcClusterLoc(fragPos, viewSpaceZ));
|
|
}
|
|
|
|
vec3 ApplyClusteredLights(const vec3 mapNormal, const vec3 eyeV, const vec3 F0, const float roughness, const vec3 IDiffuse, const vec3 firstTexture, vec3 lightSum, const float metalness)
|
|
{
|
|
// fetch cluster data (i.e. offset to light indices, and numer of lights) from grid buffer.
|
|
uvec2 offsetCount = texelFetch(clusterGridTex, calcClusterOffset(gl_FragCoord.xy, v.z)).xy;
|
|
|
|
int lightOffset = int(offsetCount.x);
|
|
int lightCount = int(offsetCount.y);
|
|
|
|
for (int i = 0; i < lightCount; ++i)
|
|
{
|
|
// fetch light index from list of lights for the cluster.
|
|
int lightIndex = texelFetch(clusterLightIndexListsTex, lightOffset + i).x;
|
|
LightValues lightValues = CalculateLightValuesClustered(lightIndex);
|
|
// compute and accumulate shading.
|
|
lightSum = ApplyPBRLight(clusteredLights[lightIndex].diffuse.rgb*2, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse, firstTexture, lightSum, metalness);
|
|
}
|
|
|
|
return lightSum;
|
|
}
|
|
#endif
|
|
|
|
void main(void)
|
|
{
|
|
float fogFactor = 0;
|
|
if (enableFog && fogMode > 0)
|
|
{
|
|
//Radial fog
|
|
float z = length(v);
|
|
if (fogMode == 3)
|
|
{
|
|
fogFactor = (fogEnd - z) * fogScale;
|
|
}
|
|
else if (fogMode == 1)
|
|
{
|
|
fogFactor = 1.0/exp(glFog.density * z);
|
|
}
|
|
else if (fogMode == 2)
|
|
{
|
|
fogFactor = 1.0 /exp( (z * glFog.density)* (z * glFog.density));
|
|
}
|
|
|
|
fogFactor = clamp(fogFactor, 0.0, 1.0);
|
|
}
|
|
|
|
vec3 eyeV=normalize(eyeVec);
|
|
vec3 normal=normalize(N);
|
|
|
|
vec2 texCoordsProcessed;
|
|
|
|
#if USE_PARALLAX_MAPS == 1
|
|
if (enableParallaxMaps)
|
|
texCoordsProcessed = ParallaxMapping(eyeV, texCoords.st);
|
|
else
|
|
#endif
|
|
texCoordsProcessed = texCoords.st;
|
|
|
|
vec4 firstTexture;
|
|
vec4 lightSum = vec4(0);
|
|
if (enableFirstTexture)
|
|
{
|
|
firstTexture = texture2D(tex, texCoordsProcessed);
|
|
if (enableSecondTexture)
|
|
{
|
|
if(tex2Mode == 0)
|
|
{
|
|
firstTexture *= texture2D(tex2, texCoords.pq);
|
|
if (enableTex2Modulate2X)
|
|
firstTexture.rgb*=4.6; //2^2.2 since rendering is gamma correct
|
|
|
|
}
|
|
else
|
|
{
|
|
lightSum.rgb += texture2D(tex2, texCoords.pq).rgb;
|
|
}
|
|
}
|
|
|
|
}
|
|
else{
|
|
firstTexture = vec4(1,1,1,1);
|
|
}
|
|
|
|
float alpha = alphaValue*firstTexture.a;
|
|
if (enableAlphaTest)
|
|
if(alphaTestCompareMethod==5)
|
|
{
|
|
if( alpha <= alphaTestValue)
|
|
discard;
|
|
}
|
|
else if(alphaTestCompareMethod==2)
|
|
{
|
|
if( alpha > alphaTestValue)
|
|
discard;
|
|
}
|
|
else
|
|
firstTexture =vec4(1,0,0,1);
|
|
|
|
vec3 mapNormal;
|
|
if (enableNormalMaps)
|
|
{
|
|
vec3 textureNormal = texture2D(normalMap, texCoordsProcessed).rgb;
|
|
textureNormal.xy = (textureNormal.rg * 2.0 - 1.0);
|
|
textureNormal.z = sqrt(1.0-dot(textureNormal.xy,textureNormal.xy));
|
|
mapNormal = normalize(textureNormal.xyz);
|
|
}
|
|
else
|
|
mapNormal = normal;
|
|
|
|
lightSum+=vec4(((matEmissiveColor.rgb))* firstTexture.rgb, alpha);
|
|
|
|
float metalness;
|
|
if (enableMetalnessMap)
|
|
{
|
|
metalness=texture2D(metalnessMap, texCoordsProcessed.st).r;
|
|
}
|
|
else
|
|
{
|
|
metalness=metalnessConst;
|
|
|
|
}
|
|
|
|
vec3 IDiffuse = (matDiffuseColor)*firstTexture.rgb;
|
|
|
|
float roughness;
|
|
if (enableRoughnessMap)
|
|
{
|
|
roughness=clamp(roughnessTextureBias+texture2D(roughnessMap, texCoordsProcessed.st).r,0.0,1.0);
|
|
}
|
|
else
|
|
{
|
|
roughness=roughnessConst;
|
|
}
|
|
|
|
#define DIELECTRIC_SPECULAR 0.04
|
|
vec3 F0 = mix(vec3(DIELECTRIC_SPECULAR), IDiffuse, metalness);
|
|
if (enableEnvMap)
|
|
{
|
|
vec3 n;
|
|
if (enableNormalMaps)
|
|
n=normalize( worldRotMat * (tbnMatrixObj * mapNormal ));
|
|
else
|
|
n=normalize(worldNormal);
|
|
vec3 v = normalize( camPos - vWorld);
|
|
vec3 reflectDir = reflect(-v, n);
|
|
const float MAX_REFLECTION_LOD = 7;
|
|
float lod = roughness * (2.0 - roughness) * MAX_REFLECTION_LOD;
|
|
vec3 irradiance;
|
|
vec3 radiance;
|
|
if (enableFog)
|
|
{
|
|
vec3 ambientTint=PBR_FACTOR_ENV*glFog.color.rgb;
|
|
irradiance = vec3(texture(irradianceMap, n).r)*ambientTint;
|
|
radiance = vec3(textureLod(envMap, reflectDir, lod).r)*ambientTint;
|
|
}
|
|
else
|
|
{
|
|
irradiance = texture(irradianceMap, n).rgb*PBR_FACTOR_ENV;
|
|
radiance = textureLod(envMap, reflectDir, lod).rgb*PBR_FACTOR_ENV;
|
|
}
|
|
|
|
float NdotV = max(dot(n,v),0.0);
|
|
vec2 brdf = texture(brdfLUT, vec2(NdotV, roughness)).rg;
|
|
|
|
#ifdef IBL_MULTIPLE_SCATTERING
|
|
vec3 diffuseColor = mix(IDiffuse.rgb, vec3(0), metalness);
|
|
// Multiple scattering, from:
|
|
//"A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting", Carmelo J. Fdez-Agüera
|
|
vec3 kS = F0;
|
|
|
|
vec3 FssEss = kS * brdf.x + brdf.y;
|
|
|
|
float Ess = brdf.x + brdf.y;
|
|
float Ems = 1.0-Ess;
|
|
vec3 Favg = F0 + (1.0-F0)/21.0;
|
|
vec3 Fms = FssEss*Favg/(1.0-Ems*Favg);
|
|
vec3 FmsEms = Fms * Ems;
|
|
// Dielectrics
|
|
vec3 Edss = 1.0 - (FssEss + FmsEms);
|
|
vec3 kD = diffuseColor * Edss;
|
|
// Composition
|
|
lightSum.rgb += FssEss * radiance + (FmsEms+kD) * irradiance;
|
|
#else
|
|
vec3 diffuse = irradiance * IDiffuse;
|
|
vec3 F = fresnelSchlickRoughness(NdotV, F0, roughness);
|
|
#ifndef SIMPLE_KD
|
|
vec3 kD=mix(vec3(1.0)-F,vec3(0.0),metalness);
|
|
#else
|
|
float kD = 1.0 - metalness;
|
|
#endif
|
|
|
|
vec3 specular = radiance * (F0 * brdf.x + brdf.y);
|
|
lightSum.rgb += kD*diffuse + specular;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (enableNormalMaps)
|
|
{
|
|
vec3 dir = vec3(0,0,1);
|
|
float ambientIntensity = dot(dir,mapNormal);
|
|
lightSum.rgb+=matAmbientColor.rgb * ambientColor.rgb* ambientIntensity*IDiffuse.rgb;
|
|
}
|
|
else
|
|
lightSum.rgb+=matAmbientColor.rgb * ambientColor.rgb* IDiffuse.rgb;
|
|
}
|
|
|
|
//Unrolled loop and a separated shadow pass to help some glsl compilers. Also some glsl compilers need a constant value (not variable!) for sampler arrays.
|
|
if(lightCount > 0)
|
|
{
|
|
const int lightIndex = 0;
|
|
LightValues lightValues = CalculateLightValues(lightIndex);
|
|
#if MAX_SHADOWS > 0
|
|
if (shadowCount > 0)
|
|
lightValues.attenFactor = ShadowCalculationForAllSplitPlanes(lightIndex, lightValues.lightDirViewSpace, normal, lightValues.attenFactor, shadowMap[0]);
|
|
#endif
|
|
lightSum.rgb = ApplyLight(lightIndex, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
}
|
|
if(lightCount > 1)
|
|
{
|
|
const int lightIndex = 1;
|
|
LightValues lightValues = CalculateLightValues(lightIndex);
|
|
#if MAX_SHADOWS > 1
|
|
if (shadowCount > 1)
|
|
lightValues.attenFactor = ShadowCalculationForAllSplitPlanes(lightIndex, lightValues.lightDirViewSpace, normal, lightValues.attenFactor, shadowMap[1]);
|
|
#endif
|
|
lightSum.rgb = ApplyLight(lightIndex, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
}
|
|
if (lightCount > 2)
|
|
{
|
|
const int lightIndex = 2;
|
|
LightValues lightValues = CalculateLightValues(lightIndex);
|
|
lightSum.rgb = ApplyLight(lightIndex, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
}
|
|
if (lightCount > 3)
|
|
{
|
|
const int lightIndex = 3;
|
|
LightValues lightValues = CalculateLightValues(lightIndex);
|
|
lightSum.rgb = ApplyLight(lightIndex, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
}
|
|
if (lightCount > 4)
|
|
{
|
|
const int lightIndex = 4;
|
|
LightValues lightValues = CalculateLightValues(lightIndex);
|
|
lightSum.rgb = ApplyLight(lightIndex, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
}
|
|
if (lightCount > 5)
|
|
{
|
|
const int lightIndex = 5;
|
|
LightValues lightValues = CalculateLightValues(lightIndex);
|
|
lightSum.rgb = ApplyLight(lightIndex, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
}
|
|
if (lightCount > 6)
|
|
{
|
|
const int lightIndex = 6;
|
|
LightValues lightValues = CalculateLightValues(lightIndex);
|
|
lightSum.rgb = ApplyLight(lightIndex, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
}
|
|
if (lightCount > 7)
|
|
{
|
|
const int lightIndex = 7;
|
|
LightValues lightValues = CalculateLightValues(lightIndex);
|
|
lightSum.rgb = ApplyLight(lightIndex, lightValues, mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
}
|
|
#if USE_CLUSTERED_SHADING == 1
|
|
if(enableClusteredShading)
|
|
lightSum.rgb = ApplyClusteredLights(mapNormal, eyeV, F0, roughness, IDiffuse.rgb, firstTexture.rgb, lightSum.rgb, metalness);
|
|
#endif
|
|
|
|
vec4 finalColor = lightSum;
|
|
|
|
|
|
if (enableFog && fogMode > 0)
|
|
{
|
|
finalColor = mix(vec4(glFog.color,1.0),finalColor, fogFactor);
|
|
}
|
|
|
|
diffuseColorOut=vec4(ToGammaCorrected(finalColor.rgb),finalColor.a);
|
|
} |