diff options
author | 3gg <3gg@shellblade.net> | 2025-06-27 10:18:39 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-06-27 10:18:39 -0700 |
commit | bd57f345ed9dbed1d81683e48199626de2ea9044 (patch) | |
tree | 4221f2f2a7ad2244d2e93052bd68187ec91b8ea9 /shaders/cook_torrance.frag | |
parent | 9a82ce0083437a4f9f58108b2c23b957d2249ad8 (diff) |
Diffstat (limited to 'shaders/cook_torrance.frag')
-rw-r--r-- | shaders/cook_torrance.frag | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/shaders/cook_torrance.frag b/shaders/cook_torrance.frag new file mode 100644 index 0000000..1975491 --- /dev/null +++ b/shaders/cook_torrance.frag | |||
@@ -0,0 +1,255 @@ | |||
1 | precision highp float; | ||
2 | |||
3 | uniform vec4 BaseColorFactor; | ||
4 | uniform float MetallicFactor; | ||
5 | uniform float RoughnessFactor; | ||
6 | uniform vec3 EmissiveFactor; | ||
7 | |||
8 | #ifdef HAS_ALBEDO_MAP | ||
9 | uniform sampler2D BaseColorTexture; | ||
10 | #endif | ||
11 | #ifdef HAS_METALLIC_ROUGHNESS_MAP | ||
12 | uniform sampler2D MetallicRoughnessTexture; | ||
13 | #endif | ||
14 | #ifdef HAS_EMISSIVE_MAP | ||
15 | uniform sampler2D EmissiveTexture; | ||
16 | #endif | ||
17 | #ifdef HAS_OCCLUSION_MAP | ||
18 | uniform sampler2D AmbientOcclusionTexture; | ||
19 | #endif | ||
20 | #ifdef HAS_NORMAL_MAP | ||
21 | uniform sampler2D NormalMap; | ||
22 | #endif | ||
23 | |||
24 | // TODO: Handle case in which there is no sky. Pass a boolean. | ||
25 | uniform samplerCube Sky; | ||
26 | uniform samplerCube IrradianceMap; | ||
27 | uniform samplerCube PrefilteredEnvironmentMap; | ||
28 | uniform sampler2D BRDFIntegrationMap; | ||
29 | uniform float MaxReflectionLOD; | ||
30 | |||
31 | uniform vec3 CameraPosition; // World space. | ||
32 | |||
33 | // World-space position, normal and tangent. | ||
34 | in vec3 Position; | ||
35 | #ifdef HAS_NORMALS | ||
36 | in vec3 Normal; | ||
37 | #endif | ||
38 | #ifdef HAS_TANGENTS | ||
39 | in vec4 Tangent; | ||
40 | #endif | ||
41 | #ifdef HAS_TEXCOORDS | ||
42 | in vec2 Texcoord; | ||
43 | #endif | ||
44 | |||
45 | layout (location = 0) out vec4 Colour; | ||
46 | |||
47 | #define PI 3.1415926535897932384626433832795 | ||
48 | #define INV_PI 0.3183098861837907 | ||
49 | |||
50 | /// Transform a normal map sample into world space. | ||
51 | /// | ||
52 | /// |normalWs| is the surface normal in world space. | ||
53 | /// |normalMapSample| is the normal map sample, not necessarily normalized. | ||
54 | /// | ||
55 | /// TODO: Move to "normal.h" | ||
56 | #if defined(HAS_NORMAL_MAP) && (defined(HAS_TANGENTS) || defined(HAS_TEXCOORDS)) | ||
57 | vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { | ||
58 | vec3 N = normalize(Normal); | ||
59 | #ifdef HAS_TANGENTS | ||
60 | //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N); | ||
61 | vec3 T = Tangent.xyz; | ||
62 | vec3 B = Tangent.w * cross(N, T); | ||
63 | #elif HAS_TEXCOORDS // No tangents, but must have texcoords. | ||
64 | vec3 pos_dx = dFdx(Position); | ||
65 | vec3 pos_dy = dFdy(Position); | ||
66 | // vec3 uv_dx = vec3(dFdx(Texcoord), 0.0); | ||
67 | // vec3 uv_dy = vec3(dFdy(Texcoord), 0.0); | ||
68 | vec3 uv_dx = dFdx(vec3(Texcoord, 0.0)); | ||
69 | vec3 uv_dy = dFdy(vec3(Texcoord, 0.0)); | ||
70 | vec3 T = (uv_dy.t * pos_dx - uv_dx.t * pos_dy) / | ||
71 | (uv_dx.s * uv_dy.t - uv_dy.s * uv_dx.t); | ||
72 | // vec3 T = pos_dx * uv_dy.t - pos_dy * uv_dx.t; | ||
73 | T = normalize(T - dot(T, N) * N); | ||
74 | vec3 B = normalize(cross(N, T)); | ||
75 | #endif | ||
76 | |||
77 | if (gl_FrontFacing == false) { | ||
78 | T = -T; | ||
79 | B = -B; | ||
80 | N = -N; | ||
81 | } | ||
82 | |||
83 | vec3 s = normalMapSample; | ||
84 | //return normalize(s.x * T + s.y * B + s.z * N); | ||
85 | return normalize(mat3(T,B,N) * s); | ||
86 | } | ||
87 | #endif // HAS_TANGENTS || HAS_TEXCOORDS | ||
88 | |||
89 | float trowbridge_reitz_GGX(float roughness, float NdotH) { | ||
90 | float a = roughness * roughness; | ||
91 | float a2 = a * a; | ||
92 | float d = NdotH * NdotH * (a2 - 1.0) + 1.0; | ||
93 | return a2 / (PI * d * d); | ||
94 | } | ||
95 | |||
96 | float geometry_schlick_GGX(float k, float NdotV) { | ||
97 | return NdotV / (NdotV * (1.0 - k) + k); | ||
98 | } | ||
99 | |||
100 | float geometry_smith(float roughness, float NdotL, float NdotV) { | ||
101 | float k = roughness * roughness / 2.0; // IBL | ||
102 | return geometry_schlick_GGX(k, NdotV) * geometry_schlick_GGX(k, NdotL); | ||
103 | } | ||
104 | |||
105 | vec3 fresnel_schlick(vec3 F0, float HdotV) { | ||
106 | return F0 + (1.0 - F0) * pow(clamp(1.0 - HdotV, 0.0, 1.0), 5.0); | ||
107 | } | ||
108 | |||
109 | vec3 fresnel_schlick_roughness(vec3 F0, float NdotV, float roughness) { | ||
110 | return F0 | ||
111 | + (max(vec3(1.0 - roughness), F0) - F0) | ||
112 | * pow(clamp(1.0 - NdotV, 0.0, 1.0), 5.0); | ||
113 | } | ||
114 | |||
115 | // Cook-Torrance BRDF for a single light direction. | ||
116 | vec3 cook_torrance( | ||
117 | vec3 albedo, float metallic, float roughness, | ||
118 | float NdotL, float NdotV, float NdotH, float HdotV) { | ||
119 | vec3 F0 = mix(vec3(0.04), albedo, metallic); | ||
120 | float D = trowbridge_reitz_GGX(roughness, NdotH); | ||
121 | vec3 F = fresnel_schlick(F0, HdotV); | ||
122 | float G = geometry_smith(roughness, NdotL, NdotV); | ||
123 | vec3 Kd = mix(vec3(1.0) - F, vec3(0.0), metallic); | ||
124 | vec3 diffuse = Kd*albedo*INV_PI; | ||
125 | // Take a max to prevent division by 0 when either dot product is 0. | ||
126 | vec3 specular = (D*F*G) / max(4.0 * NdotV * NdotL, 0.0001); | ||
127 | return diffuse + specular; | ||
128 | } | ||
129 | |||
130 | // Cook-Torrance BRDF for IBL. | ||
131 | vec3 cook_torrance_IBL( | ||
132 | vec3 albedo, float metallic, float roughness, float occlusion, | ||
133 | float NdotV, | ||
134 | vec3 irradiance, vec3 prefiltered_env, vec2 BRDF_env) { | ||
135 | vec3 F0 = mix(vec3(0.04), albedo, metallic); | ||
136 | vec3 F = fresnel_schlick_roughness(F0, NdotV, roughness); | ||
137 | vec3 Kd = mix(vec3(1.0) - F, vec3(0.0), metallic); | ||
138 | vec3 diffuse = Kd * albedo * INV_PI * irradiance; | ||
139 | vec3 specular = prefiltered_env * (F * BRDF_env.x + BRDF_env.y); | ||
140 | return occlusion * (diffuse + specular); | ||
141 | } | ||
142 | |||
143 | void main() | ||
144 | { | ||
145 | // TODO: Also use the specular F0 map from the model, and emissive. Make sure | ||
146 | // to use all maps. | ||
147 | // https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4 | ||
148 | #ifdef HAS_NORMAL_MAP | ||
149 | vec3 normalMapSample = texture(NormalMap, Texcoord).xyz * 2.0 - 1.0; | ||
150 | vec3 N = get_ws_normal(Normal, normalMapSample); | ||
151 | #elif HAS_NORMALS | ||
152 | vec3 N = normalize(Normal); | ||
153 | #endif | ||
154 | vec3 V = normalize(CameraPosition - Position); | ||
155 | vec3 R = reflect(-V, N); | ||
156 | // Not needed for IBL. | ||
157 | //vec3 L = N; | ||
158 | //vec3 H = normalize(L + V); | ||
159 | |||
160 | float NdotV = max(0.0, dot(N, V)); | ||
161 | // Not needed for IBL. | ||
162 | //float NdotL = max(0.0, dot(N,L)); | ||
163 | //float NdotH = max(0.0, dot(N,H)); | ||
164 | //float HdotV = clamp(dot(H,V), 0.0, 1.0); // Clamp to prevent black spots. | ||
165 | |||
166 | // TODO: BaseColorFactor and BaseColorTexture are vec4/rgba quantities | ||
167 | // respectively. Handle the alpha channel. | ||
168 | // TODO: Other factors. | ||
169 | #ifdef HAS_ALBEDO_MAP | ||
170 | vec3 albedo = vec3(BaseColorFactor) * texture(BaseColorTexture, Texcoord).rgb; | ||
171 | #else | ||
172 | vec3 albedo = vec3(BaseColorFactor); | ||
173 | #endif | ||
174 | #ifdef HAS_METALLIC_ROUGHNESS_MAP | ||
175 | // Spec: "Its green channel contains roughness values and its blue channel | ||
176 | // contains metalness values." | ||
177 | vec2 metal_roughness | ||
178 | = vec2(MetallicFactor, RoughnessFactor) | ||
179 | * texture(MetallicRoughnessTexture, Texcoord).bg; | ||
180 | #else | ||
181 | vec2 metal_roughness = vec2(MetallicFactor, RoughnessFactor); | ||
182 | #endif | ||
183 | #ifdef HAS_EMISSIVE_MAP | ||
184 | vec3 emissive = EmissiveFactor * texture(EmissiveTexture, Texcoord).rgb; | ||
185 | #else | ||
186 | vec3 emissive = EmissiveFactor; | ||
187 | #endif | ||
188 | #ifdef HAS_OCCLUSION_MAP | ||
189 | float occlusion = texture(AmbientOcclusionTexture, Texcoord).r; | ||
190 | #else | ||
191 | float occlusion = 1.0; | ||
192 | #endif | ||
193 | float metallic = metal_roughness.x; | ||
194 | float roughness = metal_roughness.y; | ||
195 | |||
196 | // For a single light direction: | ||
197 | // vec3 brdf = cook_torrance(albedo, metallic, roughness, NdotL, NdotV, NdotH, HdotV); | ||
198 | // vec3 Li = texture(Sky, N).rgb; | ||
199 | // vec3 colour = brdf * Li * NdotL; | ||
200 | |||
201 | // For IBL: | ||
202 | vec3 irradiance = texture(IrradianceMap, N).rgb; | ||
203 | vec3 prefiltered_env = textureLod(PrefilteredEnvironmentMap, R, roughness * MaxReflectionLOD).rgb; | ||
204 | vec2 BRDF_env = texture(BRDFIntegrationMap, vec2(NdotV, roughness)).rg; | ||
205 | vec3 colour = cook_torrance_IBL( | ||
206 | albedo, metallic, roughness, occlusion, NdotV, irradiance, prefiltered_env, BRDF_env); | ||
207 | |||
208 | colour += emissive; | ||
209 | |||
210 | // Reinhard tone mapping. | ||
211 | colour = colour / (colour + vec3(1.0)); | ||
212 | |||
213 | // Gamma correction. | ||
214 | colour = pow(colour, vec3(1.0 / 2.2)); | ||
215 | |||
216 | // Debugging. | ||
217 | // | ||
218 | // Normal texture. | ||
219 | //colour = normalMapSample * 0.5 + 0.5; | ||
220 | // | ||
221 | // Geometry normal. | ||
222 | //colour = normalize(Normal) * 0.5 + 0.5; | ||
223 | // | ||
224 | // Shading normal. | ||
225 | //colour = N * 0.5 + 0.5; | ||
226 | // | ||
227 | // Tangent and bitangent. | ||
228 | // { | ||
229 | // vec3 pos_dx = dFdx(Position); | ||
230 | // vec3 pos_dy = dFdy(Position); | ||
231 | // // vec3 uv_dx = vec3(dFdx(Texcoord), 0.0); | ||
232 | // // vec3 uv_dy = vec3(dFdy(Texcoord), 0.0); | ||
233 | // vec3 uv_dx = dFdx(vec3(Texcoord, 0.0)); | ||
234 | // vec3 uv_dy = dFdy(vec3(Texcoord, 0.0)); | ||
235 | // vec3 T = (uv_dy.t * pos_dx - uv_dx.t * pos_dy) / | ||
236 | // (uv_dx.s * uv_dy.t - uv_dy.s * uv_dx.t); | ||
237 | // // vec3 T = pos_dx * uv_dy.t - pos_dy * uv_dx.t; | ||
238 | // vec3 N = normalize(Normal); | ||
239 | // T = normalize(T - dot(T, N) * N); | ||
240 | // vec3 B = normalize(cross(N, T)); | ||
241 | |||
242 | // if (gl_FrontFacing == false) { | ||
243 | // T = -T; | ||
244 | // B = -B; | ||
245 | // N = -N; | ||
246 | // } | ||
247 | |||
248 | // // Tangent. | ||
249 | // //colour = T * 0.5 + 0.5; | ||
250 | // // Bitangent. | ||
251 | // //colour = B * 0.5 + 0.5; | ||
252 | // } | ||
253 | |||
254 | Colour = vec4(colour, 1.0); | ||
255 | } | ||