Hi, I’m having a problem with normal map on my 2D Unity project.
I need to use normals on this object called “Player”,
but for some reason it seems that the Player object reads only the red channel of the normal, leaving completely black everything else. It’s not a normal problem, since I painted them by hand and they work on any other object. (In the image below you can see how the normal map behave on the problematic Player object and on the left an object with the normal map that works fine). I also know that the problem is the Player object parent itself, since if i put the working object as a child of the player object it shows the same problem (second image).
I’ve tried to switch off all the components to find out if the problem is caused by them, but the weird behaviour always appears. Not even removing all the other child works.
It’s just on that specific object that doesn’t works.
It’s not a sorting layer problem because all lights are set to target the right layers.
The normal itself is set as a sprite and it’s set as a secondary texture.
I’m quite new at making games and I don’t know if this problem is related to rendering, something that collides with the normal or something else.
I know that the only thing that is read is the red channel because I’ve tried to switch the channels on the normal map via Unity and the red channel is the only one that is always “switched on”.
In the images it clear that the in the Player object only the red channel works while on the other object also green and blue channel are taken into account.
The whole game is in gamma space not in linear. In linear color space the normal behave in weird ways. Not sure why, but in 2D Unity if the color space is set to linear normals behave weirdly, even the one I used for tests (not done by me). Setting the color space to gamma somehow fix the weird behaviour.
The normal is done by hand on a painting program, realized taking in account the very definition of normal map (red channel = x axis inclination, green channel = y axis inclination and blue channel= z axis inclination).
Plus in the pics you sent it seems to behave right to me?
It has to work right in a top-down view so its a particular normal hand crafted for a top-down space in 3D.
So if your source light is on left or right it lighten left and right. If the source light is below it the side facing the camera gets lighted up.
If you place the light source above, the light shines on top part of the shoulders and head.
It’s built like this because the top-down view it’s a stylization of 3D space so to make light behave in a natural way I had to make some adjustments.
To check if it work as intended you must place the light source on the same plane as your quad and circle around with the light as it was in a 2D space.
The Normal is not made to take into account the Z axis (as the 2D unity light source completely ignore Z axis).
Your green channel looks wrong to me. As you say, green is the y axis inclination, with 0 being 90 degrees towards the bottom and 1 90 degrees to the top of the image, while 0.5 is no inclination in either direction. However, in your green channel, only 0 and 0.5 are found (1 only around the character), and the middle part is 0, which is why it became mostly black in josephhansen’s test.
Painting a normal map by hand is never a good idea, in my opinion, better paint a grayscale height map (for best results, use 16bit depth per channel, if possible) and generate a normal map from that. When generating the normal map, make sure to use the correct format (Y+) that Unity uses, as some tools use Y-, which is the format used by DirectX, though Unity uses Y+ regardless of the selected API.
In URP 2D’s top view, technically the sprite is facing to screen, but it’s front vector is the direction of screen bottom ish. So, the normal map’s center vector (rgb)=(0.5, 0.5, 1.0) point in the direction of screen bottom too.
I know that painting normal map by hand is not ideal, but for what I need to do I found no other solution.
The fact that the green channel ranges from 0 to .5 is indeed why I need to hand paint my normal map because I’m using them to create depth in a 2D top-down view.
The normal map is not the definitive so its not perfect, but it gives the idea.
If I used the full range of the green channel in the 6th, 7th and 8th images the top of the head of the Player would be lightened, and the turnaround effect would not work at all (it would seem that the spot light flew above the player’s head instead of “getting back” the character).
Since I have those necessity I cannot generate a Normal Map via third program.
Make sure your normal map texture import settings have the texture type set to “normal map”. If it’s set to “Default” and sRGB is enabled Unity will convert the values in the texture when using linear color space.
It does nothing, because if the colour is in gamma space (like my case) a secondary texture used as a normal can be in “Default” or “Normal” or “sprite” and the result won’t change, the normal map always works.
Still, the normal map on object Player show the usual problem.
When you set the texture import settings to “Normal map”, Unity normalizes all color pixel values to turn them into unit length vectors before compressing the image. So unless your normal map already contains proper normals (which is unlikely to be the case if you hand-painted your normal map) you really should be using “Normal” as the texture type, otherwise you’ll get wrong light intensity values.
Another possible issue: could it simply be that your light is physically behind one of the characters, but in front of the other?
Even if hand-painted the Normal works fine, it was “hand-painted” using value and the calculator on the side, plus every other object with hand-painted normal works.
Eventually I found the solution. The game is a 2D game, so they are all sprites on quads, all on the same Z-axis. I noticed that all my sprites had scale on Z set to 1 and but the Player object had scale Z set to 0. I never bothered it since Normal maps are a data set based on rgb, and as an artist I thought that they were calculated as a different set of data from the actual geometry of the quad. But how it really works is that if the Z Scale is set to 0 the vertex of the mesh collapse and with them also the Normal map ones, causing the shader to fail when trying to calculate lighting, like in my case.