aboutsummaryrefslogtreecommitdiff
path: root/shaders/cook_torrance.frag
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-06-27 10:18:39 -0700
committer3gg <3gg@shellblade.net>2025-06-27 10:18:39 -0700
commitbd57f345ed9dbed1d81683e48199626de2ea9044 (patch)
tree4221f2f2a7ad2244d2e93052bd68187ec91b8ea9 /shaders/cook_torrance.frag
parent9a82ce0083437a4f9f58108b2c23b957d2249ad8 (diff)
Restructure projectHEADmain
Diffstat (limited to 'shaders/cook_torrance.frag')
-rw-r--r--shaders/cook_torrance.frag255
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 @@
1precision highp float;
2
3uniform vec4 BaseColorFactor;
4uniform float MetallicFactor;
5uniform float RoughnessFactor;
6uniform vec3 EmissiveFactor;
7
8#ifdef HAS_ALBEDO_MAP
9uniform sampler2D BaseColorTexture;
10#endif
11#ifdef HAS_METALLIC_ROUGHNESS_MAP
12uniform sampler2D MetallicRoughnessTexture;
13#endif
14#ifdef HAS_EMISSIVE_MAP
15uniform sampler2D EmissiveTexture;
16#endif
17#ifdef HAS_OCCLUSION_MAP
18uniform sampler2D AmbientOcclusionTexture;
19#endif
20#ifdef HAS_NORMAL_MAP
21uniform sampler2D NormalMap;
22#endif
23
24// TODO: Handle case in which there is no sky. Pass a boolean.
25uniform samplerCube Sky;
26uniform samplerCube IrradianceMap;
27uniform samplerCube PrefilteredEnvironmentMap;
28uniform sampler2D BRDFIntegrationMap;
29uniform float MaxReflectionLOD;
30
31uniform vec3 CameraPosition; // World space.
32
33// World-space position, normal and tangent.
34in vec3 Position;
35#ifdef HAS_NORMALS
36in vec3 Normal;
37#endif
38#ifdef HAS_TANGENTS
39in vec4 Tangent;
40#endif
41#ifdef HAS_TEXCOORDS
42in vec2 Texcoord;
43#endif
44
45layout (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))
57vec3 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
89float 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
96float geometry_schlick_GGX(float k, float NdotV) {
97 return NdotV / (NdotV * (1.0 - k) + k);
98}
99
100float 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
105vec3 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
109vec3 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.
116vec3 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.
131vec3 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
143void 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}