Repository navigation
Expand file tree
/
Copy pathpbr.fs
More file actions
166 lines (135 loc) · 5.54 KB
/
Copy pathpbr.fs
File metadata and controls
166 lines (135 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#version 420 core
#include "frag_uniforms.glsl"
struct Material {
vec3 baseColor;
float metallic;
float roughness;
float ao;
bool hasBaseColorMap;
sampler2D baseColorMap;
bool hasMetallicMap;
sampler2D metallicMap;
bool hasRoughnessMap;
sampler2D roughnessMap;
bool hasNormalMap;
sampler2D normalMap;
bool hasAoMap;
sampler2D aoMap;
bool hasEmissiveMap;
sampler2D emissiveMap;
};
uniform Material material;
in vec3 fnormal;
in vec3 ftangent;
in vec3 fbitangent;
in vec3 fposition;
in vec3 fview;
in vec2 ftex_coords;
out vec4 frag_color;
const float PI = 3.14159265359;
vec3 getNormalFromMap() {
if(material.hasNormalMap) {
vec3 tangentNormal = texture(material.normalMap, ftex_coords).xyz * 2.0 - 1.0;
vec3 N = normalize(fnormal);
vec3 T = normalize(ftangent);
// Gram-Schmidt process to re-orthogonalize T
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T); // Reconstruct Bitangent
mat3 TBN = mat3(T, B, N);
return normalize(TBN * tangentNormal);
} else {
return fnormal;
}
}
// ----------------------------------------------------------------------------
float DistributionGGX(vec3 N, vec3 H, float roughness) {
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// ----------------------------------------------------------------------------
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}
// ----------------------------------------------------------------------------
void main() {
vec3 albedo = pow(material.hasBaseColorMap ? texture(material.baseColorMap, ftex_coords).rgb : material.baseColor, vec3(2.2));
float metallic = material.hasMetallicMap ? texture(material.metallicMap, ftex_coords).b : material.metallic;
float roughness = material.hasRoughnessMap ? texture(material.roughnessMap, ftex_coords).g : material.roughness;
float ao = material.hasAoMap ? texture(material.aoMap, ftex_coords).r : material.ao;
vec3 N = getNormalFromMap();
vec3 V = normalize(fview - fposition);
// calculate reflectance at normal incidence; if dia-electric (like plastic) use F0
// of 0.04 and if it's a metal, use the albedo color as F0 (metallic workflow)
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
// reflectance equation
vec3 Lo = vec3(0.0);
for(int i = 0; i < light_count; ++i) {
// calculate per-light radiance. Directional lights (type 1) come from a fixed
// direction with no distance attenuation; point/spot use position + falloff.
vec3 L;
float attenuation;
if(lights[i].type == 1) {
L = normalize(-lights[i].rotation);
attenuation = 1.0;
} else {
L = normalize(lights[i].position - fposition);
float distance = length(lights[i].position - fposition);
attenuation = 1.0 / (1 + lights[i].distance_dropoff * distance * distance);
}
vec3 H = normalize(V + L);
vec3 radiance = lights[i].color * attenuation;
// Cook-Torrance BRDF
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; // + 0.0001 to prevent divide by zero
vec3 specular = numerator / denominator;
// kS is equal to Fresnel
vec3 kS = F;
// for energy conservation, the diffuse and specular light can't
// be above 1.0 (unless the surface emits light); to preserve this
// relationship the diffuse component (kD) should equal 1.0 - kS.
vec3 kD = vec3(1.0) - kS;
// multiply kD by the inverse metalness such that only non-metals
// have diffuse lighting, or a linear blend if partly metal (pure metals
// have no diffuse light).
kD *= 1.0 - metallic;
// scale light by NdotL
float NdotL = max(dot(N, L), 0.0);
// add to outgoing radiance Lo
Lo += (kD * albedo / PI + specular) * radiance * NdotL; // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again
}
//TODO IBL
vec3 ambient = vec3(0.03) * albedo * ao;
vec3 emissive = material.hasEmissiveMap ? texture(material.emissiveMap, ftex_coords).rgb : vec3(0.0);
vec3 color = ambient + Lo + emissive;
// HDR tonemapping
color = color / (color + vec3(1.0));
// gamma correct
color = pow(color, vec3(1.0/2.2));
frag_color = vec4(color, 1.0);
}