feat: add pbr support
This commit is contained in:
@@ -83,6 +83,18 @@ vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v,
|
||||
return (diffuse + specular) * light.color * light.intensity * attenuation * NoL * occlusion;
|
||||
}
|
||||
|
||||
// Analytic approximation of the split sum environment BRDF (Karis, "Physically Based Shading on Mobile").
|
||||
// Used to light the flat ambient term with the same specular response as the lights.
|
||||
vec3 envBRDFApprox(vec3 f0, float roughness, float NoV)
|
||||
{
|
||||
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 * NoV)) * r.x + r.y;
|
||||
vec2 ab = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
return f0 * ab.x + ab.y;
|
||||
}
|
||||
|
||||
// blue -> green -> red, used to visualise how many lights a tile has
|
||||
vec3 lightHeatmapColor(uint lightCount)
|
||||
{
|
||||
|
||||
@@ -3,14 +3,19 @@
|
||||
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
// Must match MaterialData in Material.h
|
||||
struct MaterialData {
|
||||
vec4 baseColor;
|
||||
vec4 metallicRoughnessEmissive;
|
||||
vec4 emissiveColor;
|
||||
vec4 metallicRoughnessNormalOcclusion;
|
||||
uint textureFilteringMode;
|
||||
uint diffuseTex;
|
||||
uint normalTex;
|
||||
uint metallicRoughnessTex;
|
||||
uint metallicTex;
|
||||
uint roughnessTex;
|
||||
uint occlusionTex;
|
||||
uint emissiveTex;
|
||||
uint _pad0;
|
||||
};
|
||||
|
||||
layout (buffer_reference, std430) readonly buffer MaterialsBuffer {
|
||||
|
||||
@@ -12,14 +12,32 @@ layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
layout (location = 3) in vec4 inTangent;
|
||||
layout (location = 4) in mat3 inTBN;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
vec3 calculateNormal(MaterialData material, uint samplerID)
|
||||
{
|
||||
vec3 n = normalize(inNormal);
|
||||
|
||||
// Meshes without tangents can't use a normal map
|
||||
if (dot(inTangent.xyz, inTangent.xyz) < 1e-8) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// Re-orthogonalize, the interpolated tangent isn't perpendicular to the normal anymore
|
||||
vec3 t = normalize(inTangent.xyz - n * dot(n, inTangent.xyz));
|
||||
vec3 b = cross(n, t) * inTangent.w;
|
||||
|
||||
vec3 tangentNormal = sampleTexture2D(material.normalTex, samplerID, inUV).rgb * 2.0 - 1.0;
|
||||
tangentNormal.xy *= material.metallicRoughnessNormalOcclusion.z;
|
||||
|
||||
return normalize(mat3(t, b, n) * tangentNormal);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
MaterialData material = pcs.sceneData.materials.data[pcs.materialID];
|
||||
uint samplerID = pcs.sceneData.materials.data[pcs.materialID].textureFilteringMode;
|
||||
uint samplerID = material.textureFilteringMode;
|
||||
|
||||
vec4 diffuse = sampleTexture2D(material.diffuseTex, samplerID, inUV) * material.baseColor;
|
||||
vec3 baseColor = diffuse.rgb;
|
||||
@@ -28,27 +46,18 @@ void main()
|
||||
// discard;
|
||||
// }
|
||||
|
||||
vec3 normal = normalize(inNormal);
|
||||
// if (inTangent != vec4(0.0)) {
|
||||
// // FIXME: sometimes Blender doesn't export tangents for some objects
|
||||
// // for some reason. When we will start computing tangents manually,
|
||||
// // this check can be removed
|
||||
// normal = sampleTexture2DLinear(material.normalTex, inUV).rgb;
|
||||
// // normal.y = 1 - normal.y; // flip to make OpenGL normal maps work
|
||||
// normal = inTBN * normalize(normal * 2.0 - 1.0);
|
||||
// normal = normalize(normal);
|
||||
// }
|
||||
vec3 n = calculateNormal(material, samplerID);
|
||||
|
||||
float metallicF = material.metallicRoughnessEmissive.r;
|
||||
float roughnessF = material.metallicRoughnessEmissive.g;
|
||||
float metallicF = material.metallicRoughnessNormalOcclusion.x;
|
||||
float roughnessF = material.metallicRoughnessNormalOcclusion.y;
|
||||
float occlusionStrength = material.metallicRoughnessNormalOcclusion.w;
|
||||
|
||||
vec4 metallicRoughness = sampleTexture2DLinear(material.metallicRoughnessTex, inUV);
|
||||
|
||||
float roughness = roughnessF * metallicRoughness.g;
|
||||
// roughness *= roughness; // from perceptual to linear
|
||||
float metallic = metallicF * sampleTexture2D(material.metallicTex, samplerID, inUV).b;
|
||||
float roughness = roughnessF * sampleTexture2D(material.roughnessTex, samplerID, inUV).g;
|
||||
roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs
|
||||
|
||||
float metallic = metallicF * metallicRoughness.b;
|
||||
float occlusion = sampleTexture2D(material.occlusionTex, samplerID, inUV).r;
|
||||
float ambientOcclusion = mix(1.0, occlusion, occlusionStrength);
|
||||
|
||||
vec3 dielectricSpecular = vec3(0.04);
|
||||
vec3 black = vec3(0.0);
|
||||
@@ -57,7 +66,6 @@ void main()
|
||||
|
||||
vec3 cameraPos = pcs.sceneData.cameraPos.xyz;
|
||||
vec3 fragPos = inPos.xyz;
|
||||
vec3 n = normal;
|
||||
vec3 v = normalize(cameraPos - fragPos);
|
||||
|
||||
// forward+: only loop over the lights the culling pass put in this pixel's tile
|
||||
@@ -70,26 +78,28 @@ void main()
|
||||
uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i];
|
||||
Light light = pcs.sceneData.lights.data[lightIndex];
|
||||
|
||||
float occlusion = 1.0;
|
||||
float shadow = 1.0;
|
||||
if (light.castShadows != 0) {
|
||||
occlusion = calculateDirectionalShadow(
|
||||
fragPos, n,
|
||||
shadow = calculateDirectionalShadow(
|
||||
fragPos, normalize(inNormal),
|
||||
pcs.sceneData.lightViewProj,
|
||||
pcs.sceneData.shadowMapId,
|
||||
pcs.sceneData.shadowNormalOffset);
|
||||
}
|
||||
|
||||
fragColor += calculateLight(light, fragPos, n, v,
|
||||
diffuseColor, roughness, metallic, f0, occlusion);
|
||||
diffuseColor, roughness, metallic, f0, shadow);
|
||||
}
|
||||
|
||||
// emissive
|
||||
float emissiveF = material.metallicRoughnessEmissive.b;
|
||||
vec3 emissiveColor = emissiveF * sampleTexture2DLinear(material.emissiveTex, inUV).rgb;
|
||||
vec3 emissiveColor = material.emissiveColor.rgb * sampleTexture2D(material.emissiveTex, samplerID, inUV).rgb;
|
||||
fragColor += emissiveColor;
|
||||
|
||||
// ambient
|
||||
fragColor += baseColor * pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
|
||||
// ambient, the specular part keeps metals from turning black without lights
|
||||
float NoV = clamp(dot(n, v), 0.0001, 1.0);
|
||||
vec3 ambientLight = pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
|
||||
vec3 ambientSpecular = envBRDFApprox(f0, roughness, NoV);
|
||||
fragColor += (diffuseColor + ambientSpecular) * ambientLight * ambientOcclusion;
|
||||
|
||||
if (pcs.sceneData.debugLightHeatmap != 0) {
|
||||
fragColor = mix(fragColor, lightHeatmapColor(tileLightCount), 0.5);
|
||||
@@ -104,7 +114,7 @@ void main()
|
||||
|
||||
#if 0
|
||||
// NORMAL DEBUG
|
||||
fragColor = normal;
|
||||
fragColor = n;
|
||||
#endif
|
||||
|
||||
outFragColor = vec4(fragColor, diffuse.a);
|
||||
|
||||
@@ -8,7 +8,6 @@ layout (location = 0) out vec3 outPos;
|
||||
layout (location = 1) out vec2 outUV;
|
||||
layout (location = 2) out vec3 outNormal;
|
||||
layout (location = 3) out vec4 outTangent;
|
||||
layout (location = 4) out mat3 outTBN;
|
||||
|
||||
// The depth pre-pass reuses this shader, positions have to match bit for bit
|
||||
// so the main pass can depth test against it with LESS_OR_EQUAL
|
||||
@@ -29,10 +28,6 @@ void main()
|
||||
// outNormal = (pcs.transform * vec4(v.normal, 0.0)).xyz;
|
||||
outNormal = mat3(transpose(inverse(pcs.transform))) * v.normal;
|
||||
|
||||
outTangent = v.tangent;
|
||||
|
||||
vec3 T = normalize(vec3(pcs.transform * v.tangent));
|
||||
vec3 N = normalize(outNormal);
|
||||
vec3 B = cross(N, T) * v.tangent.w;
|
||||
outTBN = mat3(T, B, N);
|
||||
// w is the bitangent sign, the fragment shader builds the TBN from the interpolated vectors
|
||||
outTangent = vec4(mat3(pcs.transform) * v.tangent.xyz, v.tangent.w);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user