Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically generate a specular map when there is no specular map #1243

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

illwieckz
Copy link
Member

Automatically generate a specular map when there is no specular map

Example video:

20240816-024444-001.unvanquished.webm

More video and images:

…are is too weak)

Use glConfig2 instead of cvar (it can be disabled if hardware is too weak).

Also delete an unused define (the USE_PHYSICAL_MAPPING macro is used instead).
@illwieckz illwieckz added A-Renderer T-Feature-Request Proposed new feature labels Aug 16, 2024
@illwieckz
Copy link
Member Author

illwieckz commented Aug 16, 2024

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
Copy link
Member Author

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 illwieckz force-pushed the illwieckz/automatic-specularmap branch 2 times, most recently from ec5eca9 to 3c302bc Compare August 16, 2024 01:51
@illwieckz
Copy link
Member Author

illwieckz commented Aug 16, 2024

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
Copy link
Member Author

illwieckz commented Aug 16, 2024

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.

@sweet235
Copy link
Contributor

sweet235 commented Aug 16, 2024

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

@illwieckz illwieckz force-pushed the illwieckz/automatic-specularmap branch from 3c302bc to efa5e75 Compare August 16, 2024 14:23
@@ -949,6 +949,11 @@ static bool IsUnusedPermutation( const char *compileMacros )
see https://github.com/DaemonEngine/Daemon/issues/355 */
if ( !glConfig2.specularMapping ) return true;
}
else if ( strcmp( token, "USE_AUTOMATIC_SPECULARMAPPING" ) == 0 )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro spelling is wrong

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

vec4 material = texture2D(u_MaterialMap, texCoords);
#if defined(USE_AUTOMATIC_SPECULARMAP)
float mean = (diffuse.r + diffuse.g + diffuse.b) * 0.333333;
mean = max(max(max(mean, diffuse.r), diffuse.g), diffuse.b);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines are just the same as taking the max component, i.e. float mean = max(max(diffuse.r, diffuse.g), diffuse.b)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right! 😅️ Now fixed.

@illwieckz illwieckz force-pushed the illwieckz/automatic-specularmap branch from efa5e75 to 1974af7 Compare August 17, 2024 00:56
@slipher
Copy link
Member

slipher commented Aug 17, 2024

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.

@sweet235
Copy link
Contributor

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
Copy link
Contributor

sweet235 commented Aug 20, 2024

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
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants