Daemon icon indicating copy to clipboard operation
Daemon copied to clipboard

Automatically generate a specular map when there is no specular map

Open illwieckz opened this issue 1 year ago • 9 comments

Automatically generate a specular map when there is no specular map

Example video:

20240816-024444-001.unvanquished.webm

More video and images:

  • https://forums.unvanquished.net/viewtopic.php?t=2463

illwieckz avatar Aug 16 '24 00:08 illwieckz

That's something I worked on in April.

A naive way would be to convert color to greyscale but this is wrong in many situations.

First thing that is wrong in converting color to greyscale, is that full white would be 1, while full red, full green or full blue would only be 0.3. So instead of converting RGB to grey, I convert to max(mean(r, g, b), r, g, b) to grey.

Second thing that is wrong is that:

  • black is not always matte,
  • white is not always glossy.

but this is not completely wrong as legacy textures may have baked some glossiness and then brighter parts may be more glossy.

What we should avoid is to get obviously wrong reflections, like a sand beach being glossy because being light beige.

Things being wrongly matte looks far less unpleasing to the eyes than things wrongly glossy. In fact, without specular maps all surfaces are already all wrongly matte, it is still fine, and if the implemented algorithm produces something too matte than needed, at worst it just does not do better and never does wrong.

So, the idea is to reach some average specularity value of concrete. Metal may be not shiny enough, but all other surfaces would not be too shiny. Also we still want full back to have no specularity at all, as full black is used as a trick for undrawn things (like the bottom of a pit).

I want the emulated specularity to be very subtle: it is not meant to be noticed, it is meant to be felt better.

Something I dislike a lot in early games implementing specularity, is that they made sure we noticed it, the surfaces were yelling at the player “look! look! I implemented specular mapping! look at all this shininess! loook!”, but in real life the concrete is not supposed to yell at you “look! look at my shininess, do you notice my gorgeous specular map?”. And since it's emulated and we don't want to make it obviously too shiny, focusing on being very subtle is a good idea.

The targets are:

  • While moving, a bit of light reflection moves with the point of view, always being between the light source and the point of view.
  • The shininess is not even on the whole surface, for example an embossed metal floor should not reflect light like a flat one.

It happens that the color variations of a surface are a good data to build shininess variation: different color may be different material, different paint, with different shininess, color changes may even be dust or corrosion. That's part of why converting color to greyscale is not a bad idea per se, the shininess change follows the color change patterns. For example a white and black checkered floor will get checkered shininess with different shininess on white and black tiles but same shininess on all white tiles and same shininess on all black tiles.

Assuming a darker color is less shiny isn't bad, especially since legacy textures may bake some holes filled with shadows, and some reflections, and we want such shadowed holes to be less shiny and reflections to be more shiny.

So what I came up with was:

  • Convert max(mean(r, g, b), r, g, b) to grey: full white has same shininess as full red, etc.
  • Divide by 0.3: make sur the shininess is never more than this arbitrary value, and black is always matte, while preserving variations.
  • Shift down by 0.05: make sure some values above black also gets matte, this also lowers a bit more the global shininess.
  • Floor to 0: to make sure we never gets any negative shininess because of the previous shift.

The values of 0.3 and 0.05 are purely arbitrary and based on testing of many maps and surfaces. Basically I lowered down until I stopped to see obviously wrong shininess, while making sure I was still spotting light reflection following the point of view and shininess variations.

illwieckz avatar Aug 16 '24 01:08 illwieckz

There are currently two additional commits that are just fixes I noticed while rebasing and rewriting some code to leave the proof-of-concept state, I plan to submit those two other commits in separate PRs.

illwieckz avatar Aug 16 '24 01:08 illwieckz

My initial factor of 0.4 was still too strong on both soil and metal outdoor floors of ATCSHD. I reduced to 0.3 and now it looks as expected and the specularity effect is still noticeable for the one looking for it.

illwieckz avatar Aug 16 '24 01:08 illwieckz

It is useful to know that the feature currently only works on maps with baked deluxemaps. So it works with legacy textures but not with legacy Tremulous maps that were never rebuilt.

In the future we may use the lightgrid to emulate a deluxemap like we already do with models, this would allow us to get specularity on all maps, including legacy ones, without any texture change and map rebuild, but this is out of scope of this PR.

illwieckz avatar Aug 16 '24 01:08 illwieckz

I did not read the code. But I am currently looking at some maps. This looks great.

sweet235 avatar Aug 16 '24 08:08 sweet235

About the maximum r/g/b channel thing... what is the point of boosting the specularity of certain hues (red, green, and blue) compared to others? IIRC in chat you were saying that just using grayscale is naive or something, but at least that seems less arbitrary.

In general the feature seems hit or miss. In the nexus6 scene the pipes look nice, but the gray concrete-looking parts are brightened way too much. Basically we are putting the same specularity for all surfaces, regardless of what roughness/shininess/whatever they ought to have, and only varying with a kind of random function of pixel color, which doesn't really correlate with shininess. Based on the screenshots provided I don't find it an improvement.

I don't like how the feature generally increases the overall brightness of the map, as if r_gamma had been increased. (Though there is the same issue when toggling r_normalMapping.) If the mapper targets some specific level of brightness, it is impossible to achieve it both with and without automatic specular mapping enabled.

slipher avatar Aug 17 '24 02:08 slipher

I think the effect could be a little stronger in some cases.

Here is an example of making this effect too strong: https://forum.grangerhub.org/t/trem-with-the-ioq3-gl2-renderer-pretty/422/4

sweet235 avatar Aug 20 '24 21:08 sweet235

For comparison, I created specular maps for the atcshd materials by simply converting to grayscale. This is too complicated to link here, but you can look at it on my server. The maps are: atcshd, ptcs1, ptcs7, utcs, new-hope, pierogi, uds.

I have conducted a little survey, and people seem to think that this is neither too strong nor too weak.

sweet235 avatar Aug 20 '24 21:08 sweet235

For example, this is map uds:

unvanquished_2024-08-20_225951_000

I created the specular maps by converting to grayscale, and slightly tweaking contrast and brightness.

sweet235 avatar Aug 20 '24 22:08 sweet235

I rebased, the algorithm to guess a specular map can be refined later, I made sure it is not obviously wrong on things like the atcshd floor. The idea is to make it subtle so in worst case it would be too much subtle (which is not a problem).

This also means reflection cubemaps will have subtle effects on legacy maps too.

illwieckz avatar Oct 30 '24 14:10 illwieckz

Does this still do anything? I checked out the latest version and couldn't see any difference.

If I could, I would probably be annoyed at what I saw though. There is no way at all to determine what surfaces are supposed to be smooth, so it will inevitably lead to shiny dirt.

slipher avatar Oct 30 '24 17:10 slipher