@@ -163,6 +163,8 @@ void main()
163163 #endif
164164
165165 #if defined(USE_LIGHT_MAPPING) && defined(USE_DELUXE_MAPPING)
166+ vec3 lightmapValue = lightColor;
167+
166168 /* Lightmaps generated by q3map2 don't store the raw light value, but
167169 they store light premultiplied with the dot product of the light
168170 direction and surface normal. The line is just an attempt to reverse
@@ -183,18 +185,39 @@ void main()
183185 Increasing the value should reduce these artifacts. -- gimhael
184186 https://github.com/DaemonEngine/Daemon/issues/299#issuecomment-606186347
185187 */
186-
187- // Divide by cosine term to restore original light color.
188- lightColor /= clamp (dot (normalize (var_Normal), lightDir), 0.3 , 1.0 );
188+ vec3 flatNormal = normalize (var_Normal);
189+ float flatNdotL = clamp (dot (flatNormal, lightDir), 0.3 , 1.0 );
190+ lightColor /= flatNdotL;
191+
192+ // The light direction from the deluxe map may be nonsense, be it because q3map2
193+ // goofed, or because the light actually comes from more than one direction(!!!!). The total, summed
194+ // over light sources, of (light color) * dot(flat triangle's normal, light direction), as given by the lightmap, is more
195+ // trustworthy. If the normalmapped normal is almost the same as the the original flat geometry's normal,
196+ // we can bound how much the above sum can change, thus preventing the bogus light direction from
197+ // wrecking the Lambert's law term of our lighting equation.
198+ // So we want to compute the maximum of
199+ // abs(dot(var_Normal, L) - dot(normal, L))
200+ // = abs(dot(normal - var_Normal, L))
201+ // over all possible L. This maximum occurs when L is parallel to normal - var_Normal.
202+ float dotDelta = length (normal - flatNormal);
203+ // So each dot product term can only increase or decrease by dotDelta. But we don't know
204+ // what the original dot products were. We'll have to assume a maximum angle to be able to
205+ // bound the percent change of the total.
206+ float minimumAverageCosine = 0.1 ; // 84.3 degrees
207+ float cosinesRatio = dot (normal, lightDir) / flatNdotL;
208+ cosinesRatio = clamp ( cosinesRatio, 1.0 - dotDelta / minimumAverageCosine, 1.0 + dotDelta / minimumAverageCosine );
209+ vec3 lambertTerm = lightmapValue * cosinesRatio;
210+ #else
211+ vec3 lambertTerm; // not used
189212 #endif
190213
191214 // Blend static light.
192215 #if defined(USE_DELUXE_MAPPING) || defined(USE_GRID_DELUXE_MAPPING)
193216 #if defined(USE_REFLECTIVE_SPECULAR)
194217 vec4 modifiedSpecular = material * EnvironmentalSpecularFactor(viewDir, normal);
195- computeDeluxeLight(lightDir, normal, viewDir, lightColor, diffuse, modifiedSpecular, color);
218+ computeDeluxeLight(lightDir, normal, viewDir, lightColor, lambertTerm, diffuse, modifiedSpecular, color);
196219 #else // !USE_REFLECTIVE_SPECULAR
197- computeDeluxeLight(lightDir, normal, viewDir, lightColor, diffuse, material, color);
220+ computeDeluxeLight(lightDir, normal, viewDir, lightColor, lambertTerm, diffuse, material, color);
198221 #endif // !USE_REFLECTIVE_SPECULAR
199222 #else
200223 computeLight(lightColor, diffuse, color);
0 commit comments