@FrozenShade said:
It's after midnight here and playing is the only thing that left to me now. dot(norm, lightdir) is the cosine between these two vectors so now val is the sinus. To be honest some of my attempts are just blind shots.
Being pedantic as it doesn’t really matter to your issue, but should matter to your long term understanding… but 1 - cosine is not sine.
cos^2 + sin^2 = 1
sin^2 = 1 - cos^2
sin = sqrt(1 - cos^2)
1 - cos is not simply described. For example, if the angle was 45 degrees then the resulting angle is 72 degrees. 0 becomes 90 and 90 becomes 0 but all other angles are kind of skewed. 15 degrees becomes 88 degrees. 75 degrees becomes 42 degrees. I’m tempted to plot the curve to see what it looks like. There is probably a stretched reflection point in there somewhere. (Ah, reflects at 60 degrees… cos = 0.5) 60 degrees becomes 60 degrees.
In your latest code:
@FrozenShade said:
float val = 1.0 - max(0.0, dot(norm, lightdir));
return ((1.0 - (vLightDir.w * val)) + vLightDir.w * 0.3) * 0.85;
Just reworking the math a bit to try to understand it…
val = 1.0 - max(0.0, dot(norm, lightdir);
…let’s call that…
val = 1.0 - cosTheta
1.0 - (vLightDir.w * val)
…let’s call that…
1.0 - (w * val)
so:
1.0 - (w * (1.0 - cosTheta))
or:
1.0 + (w * (1.0 - cosTheta) * -1.0)
or:
1.0 + (w * (cosTheta - 1))
or:
1.0 + (w * costTheta) - w
So now all together:
(1.0 + (w * cosTheta) - w + (w * 0.3)) * 0.85
Let’s see what it looks like as cosines:
(cos(90) + (w * cosTheta) - w + (w * cos(72.5))) * cos(31.7)
Anyway, as fun as that was… it isn’t really illuminating. The result is used as a percentage, sort of… so the cosine can get lost early. Though I guess the reduction still works nicely that way:
(1.0 + atten * diffuseFactor - atten + (atten * 0.3)) * 0.85
…which is:
0.85 + (0.85 * atten * diffuseFactor) - (atten * 0.85) + (atten * 0.3 * 0.85)
…
0.85 + (0.85 * atten * diffuseFactor) - (atten * 0.85) + (atten * 0.255)
…
0.85 + atten * ((0.85 * diffuseFactor) - 0.85 + 0.255)
…
0.85 + atten * ((0.85 * diffuseFactor) - 0.595)
Which is a much simpler version of the same thing (if my math is right).
Hmmm… I maybe can do even better…
0.85 + (atten * ((0.85 * diffuseFactor) - 0.595))
…is also:
0.85 + (atten * 0.85 * ((0.85 * diffuseFactor)/0.85 - 0.595/0.85))
…
0.85 + (atten * 0.85 * (diffuseFactor - 0.595/0.85))
…
0.85 + (atten * 0.85 * (diffuseFactor - 0.7))
…and I think we can go one more because that duplicate 0.85 now bugs me…
0.85 * (1 + (atten * (diffuseFactor - 0.7)))
It’s much easier to see what the characteristics are now… and if you ever decide you want to clamp the overflow at something other than the final result you have sensible places to do it now. Though it’s all a bit ‘trial and error’ random so it’s hard to say what would make sense.
So if diffuseFactor is between 0 and 1, with normally 0 meaning no light at all and 1 meaning fully facing the light… it seems like now that is put into the range of -0.7 to 0.3, respectively. Then multiplied by distance which will also be between 0 and 1. So, whatever the angle was, it will be pushed towards 45 degrees as distance increases. ie: at max distance, the result there is always 0 (which was originally 0.7, acos(0.7) is a bit more than 45 degrees). So anyway, the angle is now relative to ~45 degrees and tapered based on distance. And the (1 + foo) * 0.85 part is moving that deflection to 31 degrees.
I guess it looks like you will never go below 0 but for direct light close to the surface you will spike above 1 by as much as 0.15 (if atten is ever 1.0). If this actually does occur then post-clamping may create noticeable circle where the light goes flat. In which case, you could premultiply the (atten * (diffuseFactor - 0.7)) part by 0.5 to prevent it, I guess. It would make the dark places brighter, though. Maybe given the atten values it’s not an issue anyway and perhaps the spike is visually desirable.
Interesting thing you arrived at by blind luck.