mirror of
https://github.com/bloeys/nmage.git
synced 2025-12-29 13:28:20 +00:00
265 lines
7.5 KiB
GLSL
Executable File
265 lines
7.5 KiB
GLSL
Executable File
//shader:vertex
|
|
#version 410
|
|
|
|
layout(location=0) in vec3 vertPosIn;
|
|
layout(location=1) in vec3 vertNormalIn;
|
|
layout(location=2) in vec2 vertUV0In;
|
|
layout(location=3) in vec3 vertColorIn;
|
|
|
|
out vec3 vertNormal;
|
|
out vec2 vertUV0;
|
|
out vec3 vertColor;
|
|
out vec3 fragPos;
|
|
out vec4 fragPosDirLight;
|
|
|
|
uniform mat4 modelMat;
|
|
uniform mat4 projViewMat;
|
|
uniform mat4 dirLightProjViewMat;
|
|
|
|
void main()
|
|
{
|
|
// @TODO: Calculate this on the CPU and send it as a uniform
|
|
//
|
|
// This produces the normal matrix that multiplies with the model normal to produce the
|
|
// world space normal. Based on 'One last thing' section from: https://learnopengl.com/Lighting/Basic-Lighting
|
|
vertNormal = mat3(transpose(inverse(modelMat))) * vertNormalIn;
|
|
|
|
vertUV0 = vertUV0In;
|
|
vertColor = vertColorIn;
|
|
|
|
vec4 modelVert = modelMat * vec4(vertPosIn, 1);
|
|
fragPos = modelVert.xyz;
|
|
fragPosDirLight = dirLightProjViewMat * vec4(fragPos, 1);
|
|
|
|
gl_Position = projViewMat * modelVert;
|
|
}
|
|
|
|
//shader:fragment
|
|
#version 410
|
|
|
|
struct Material {
|
|
sampler2D diffuse;
|
|
sampler2D specular;
|
|
// sampler2D normal;
|
|
sampler2D emission;
|
|
float shininess;
|
|
};
|
|
|
|
uniform Material material;
|
|
|
|
struct DirLight {
|
|
vec3 dir;
|
|
vec3 diffuseColor;
|
|
vec3 specularColor;
|
|
sampler2D shadowMap;
|
|
};
|
|
|
|
uniform DirLight dirLight;
|
|
|
|
struct PointLight {
|
|
vec3 pos;
|
|
vec3 diffuseColor;
|
|
vec3 specularColor;
|
|
float constant;
|
|
float linear;
|
|
float quadratic;
|
|
float farPlane;
|
|
};
|
|
|
|
#define NUM_POINT_LIGHTS 8
|
|
uniform PointLight pointLights[NUM_POINT_LIGHTS];
|
|
uniform samplerCubeArray pointLightCubeShadowMaps;
|
|
|
|
struct SpotLight {
|
|
vec3 pos;
|
|
vec3 dir;
|
|
vec3 diffuseColor;
|
|
vec3 specularColor;
|
|
float innerCutoff;
|
|
float outerCutoff;
|
|
};
|
|
|
|
#define NUM_SPOT_LIGHTS 4
|
|
uniform SpotLight spotLights[NUM_SPOT_LIGHTS];
|
|
|
|
uniform vec3 camPos;
|
|
uniform vec3 ambientColor = vec3(0.2, 0.2, 0.2);
|
|
|
|
in vec3 vertColor;
|
|
in vec3 vertNormal;
|
|
in vec2 vertUV0;
|
|
in vec3 fragPos;
|
|
in vec4 fragPosDirLight;
|
|
|
|
out vec4 fragColor;
|
|
|
|
// Global variables used as cache for lighting calculations
|
|
vec4 diffuseTexColor;
|
|
vec4 specularTexColor;
|
|
vec4 emissionTexColor;
|
|
vec3 normalizedVertNorm;
|
|
vec3 viewDir;
|
|
|
|
float CalcDirShadow(sampler2D shadowMap, vec3 lightDir)
|
|
{
|
|
// Move from clip space to NDC
|
|
vec3 projCoords = fragPosDirLight.xyz / fragPosDirLight.w;
|
|
|
|
// Move from [-1,1] to [0, 1]
|
|
projCoords = projCoords * 0.5 + 0.5;
|
|
|
|
// If sampling outside the depth texture then force 'no shadow'
|
|
if(projCoords.z > 1)
|
|
return 0;
|
|
|
|
// currentDepth is the fragment depth from the light's perspective
|
|
float currentDepth = projCoords.z;
|
|
|
|
// Bias in the range [0.005, 0.05] depending on the angle, where a higher
|
|
// angle gives a higher bias, as shadow acne gets worse with angle
|
|
float bias = max(0.05 * (1 - dot(normalizedVertNorm, lightDir)), 0.005);
|
|
|
|
// 'Percentage Close Filtering'.
|
|
// Basically get soft shadows by averaging this texel and surrounding ones
|
|
float shadow = 0;
|
|
vec2 texelSize = 1 / textureSize(shadowMap, 0);
|
|
for(int x = -1; x <= 1; ++x)
|
|
{
|
|
for(int y = -1; y <= 1; ++y)
|
|
{
|
|
float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
|
|
|
// If our depth is larger than the lights closest depth at the texel we checked (projCoords),
|
|
// then there is something closer to the light than us, and so we are in shadow
|
|
shadow += currentDepth - bias > pcfDepth ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
shadow /= 9;
|
|
|
|
return shadow;
|
|
}
|
|
|
|
vec3 CalcDirLight()
|
|
{
|
|
vec3 lightDir = normalize(-dirLight.dir);
|
|
|
|
// Diffuse
|
|
float diffuseAmount = max(0.0, dot(normalizedVertNorm, lightDir));
|
|
vec3 finalDiffuse = diffuseAmount * dirLight.diffuseColor * diffuseTexColor.rgb;
|
|
|
|
// Specular
|
|
vec3 halfwayDir = normalize(lightDir + viewDir);
|
|
float specularAmount = pow(max(dot(normalizedVertNorm, halfwayDir), 0.0), material.shininess);
|
|
vec3 finalSpecular = specularAmount * dirLight.specularColor * specularTexColor.rgb;
|
|
|
|
// Shadow
|
|
float shadow = CalcDirShadow(dirLight.shadowMap, lightDir);
|
|
|
|
return (finalDiffuse + finalSpecular) * (1 - shadow);
|
|
}
|
|
|
|
float CalcPointShadow(int lightIndex, vec3 lightPos, vec3 lightDir, float farPlane) {
|
|
|
|
vec3 lightToFrag = fragPos - lightPos;
|
|
|
|
float closestDepth = texture(pointLightCubeShadowMaps, vec4(lightToFrag, lightIndex)).r;
|
|
|
|
// We stored depth in the cubemap in the range [0, 1], so now we move back to [0, farPlane]
|
|
closestDepth *= farPlane;
|
|
|
|
// Get depth of current fragment
|
|
float currentDepth = length(lightToFrag);
|
|
|
|
float bias = max(0.05 * (1 - dot(normalizedVertNorm, lightDir)), 0.005);
|
|
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
|
|
|
|
return shadow;
|
|
}
|
|
|
|
vec3 CalcPointLight(PointLight pointLight, int lightIndex)
|
|
{
|
|
// Ignore unset lights
|
|
if (pointLight.constant == 0){
|
|
return vec3(0);
|
|
}
|
|
|
|
vec3 lightDir = normalize(pointLight.pos - fragPos);
|
|
|
|
// Diffuse
|
|
float diffuseAmount = max(0.0, dot(normalizedVertNorm, lightDir));
|
|
vec3 finalDiffuse = diffuseAmount * pointLight.diffuseColor * diffuseTexColor.rgb;
|
|
|
|
// Specular
|
|
vec3 halfwayDir = normalize(lightDir + viewDir);
|
|
float specularAmount = pow(max(dot(normalizedVertNorm, halfwayDir), 0.0), material.shininess);
|
|
vec3 finalSpecular = specularAmount * pointLight.specularColor * specularTexColor.rgb;
|
|
|
|
// Attenuation
|
|
float distToLight = length(pointLight.pos - fragPos);
|
|
float attenuation = 1 / (pointLight.constant + pointLight.linear * distToLight + pointLight.quadratic * (distToLight * distToLight));
|
|
|
|
// Shadow
|
|
float shadow = CalcPointShadow(lightIndex, pointLight.pos, lightDir, pointLight.farPlane);
|
|
|
|
return (finalDiffuse + finalSpecular) * attenuation * (1 - shadow);
|
|
}
|
|
|
|
vec3 CalcSpotLight(SpotLight light)
|
|
{
|
|
if (light.innerCutoff == 0)
|
|
return vec3(0);
|
|
|
|
vec3 fragToLightDir = normalize(light.pos - fragPos);
|
|
|
|
// Spot light cone with full intensity within inner cutoff,
|
|
// and falloff between inner-outer cutoffs, and zero
|
|
// light after outer cutoff
|
|
float theta = dot(fragToLightDir, normalize(-light.dir));
|
|
float epsilon = (light.innerCutoff - light.outerCutoff);
|
|
float intensity = clamp((theta - light.outerCutoff) / epsilon, float(0), float(1));
|
|
|
|
if (intensity == 0)
|
|
return vec3(0);
|
|
|
|
// Diffuse
|
|
float diffuseAmount = max(0.0, dot(normalizedVertNorm, fragToLightDir));
|
|
vec3 finalDiffuse = diffuseAmount * light.diffuseColor * diffuseTexColor.rgb;
|
|
|
|
// Specular
|
|
vec3 halfwayDir = normalize(fragToLightDir + viewDir);
|
|
float specularAmount = pow(max(dot(normalizedVertNorm, halfwayDir), 0.0), material.shininess);
|
|
vec3 finalSpecular = specularAmount * light.specularColor * specularTexColor.rgb;
|
|
|
|
return (finalDiffuse + finalSpecular) * intensity;
|
|
}
|
|
|
|
void main()
|
|
{
|
|
// Shared values
|
|
diffuseTexColor = texture(material.diffuse, vertUV0);
|
|
specularTexColor = texture(material.specular, vertUV0);
|
|
emissionTexColor = texture(material.emission, vertUV0);
|
|
|
|
normalizedVertNorm = normalize(vertNormal);
|
|
viewDir = normalize(camPos - fragPos);
|
|
|
|
// Light contributions
|
|
vec3 finalColor = CalcDirLight();
|
|
|
|
for (int i = 0; i < NUM_POINT_LIGHTS; i++)
|
|
{
|
|
finalColor += CalcPointLight(pointLights[i], i);
|
|
}
|
|
|
|
for (int i = 0; i < NUM_SPOT_LIGHTS; i++)
|
|
{
|
|
finalColor += CalcSpotLight(spotLights[i]);
|
|
}
|
|
|
|
vec3 finalEmission = emissionTexColor.rgb;
|
|
vec3 finalAmbient = ambientColor * diffuseTexColor.rgb;
|
|
|
|
fragColor = vec4(finalColor + finalAmbient + finalEmission, 1);
|
|
}
|