Source Filmmaker

Source Filmmaker

Shakes Oct 13, 2018 @ 8:17am
Recreating blue channel of normal map?
Long story short, trying to port a model but the blue channels for the normal maps are all black, so I'm assuming the game they're from (Quake Champions) does the blue channel in the shader. What I'm asking is if anyone here knows how to recreate the blue channels using GIMP (or other software) so I can get a normal map that doesn't give Source Engine (and Blender) an aneurism. As far as I know, Source Engine only uses RG normal maps for the "EyeRefract" shader. I've come close but the result has always been off slightly. Google has been less than helpful.

If memory serves me correctly, z = sqrt(1 - x^2 - y^2) where z is the blue channel value and x & y are the red and green values, respectively. I could do this manually for each pixel but I'd rather not do the calculation a million times (the normal maps are 1024*1024). Is there a way to automate this and generate an output image from the results? Perhaps in Python? Or is any of this actually necessary?
< >
Showing 1-15 of 24 comments
Zappy Oct 13, 2018 @ 8:22am 
An often-decent solution is to just use a value of 255 (or FF or 100%) for the blue channel, over all pixels.
Last edited by Zappy; Oct 13, 2018 @ 8:23am
Shakes Oct 13, 2018 @ 8:41am 
Originally posted by Zappy:
An often-decent solution is to just use a value of 255 (or FF or 100%) for the blue channel, over all pixels.
I did try that, but the result still looks off compared to what it looks like in the original game. I mean, I'm not expecting it to look perfect but I'd like to remove inconsistencies between engines where I can. I've figured out how to fit most of QC's textures into Source Engine texture slots but there are only two others I haven't been able to figure out yet, besides the normal map.
Zappy Oct 13, 2018 @ 8:50am 
Originally posted by Shakes:
I did try that, but the result still looks off compared to what it looks like in the original game. -
If it really looks "off", try inverting the green colour channel. Some games/programs (including Source) think that a low green normal map colour means upwards, while others think that it means downwards, and vice-versa. (Luckily, this doesn't seem to be the case for the red colour channel.)

Originally posted by Shakes:
- I'd like to remove inconsistencies between engines where I can. -
For this specific case, the only option that I can immediately think of is writing a custom program that draws a 1024x1024 image of the normal map texture's red and green values, while it computes the blue colour based on the red and green colour. (Then again, I have done something slightly similar in the past, so that may be more appealing to me than most other people.)
Shakes Oct 13, 2018 @ 9:04am 
I did invert the green. That was probably the first thing I did with the normals, actually. I figured out it was off because the top of the green channel for the eyeball normal was black while on Source Engine cornea textures the top was white.

Originally posted by Zappy:
For this specific case, the only option that I can immediately think of is writing a custom program that draws a 1024x1024 image of the normal map texture's red and green values, while it computes the blue colour based on the red and green colour. (Then again, I have done something slightly similar in the past, so that may be more appealing to me than most other people.)

That's actually what I was leaning towards. I got close with GIMP but it wasn't matching the reference image[squircleart.github.io] I was using. I was thinking of using Python but I don't really have a lot of experience beyond simple "Hello world" stuff. I did a little Jython a few years back but I don't remember any of it.
Last edited by Shakes; Oct 13, 2018 @ 9:04am
green Oct 13, 2018 @ 9:12am 
So, you're trying to make a normal map? This site might help you. It's not perfect, but it's okay.

http://cpetry.github.io/NormalMap-Online/
Zappy Oct 13, 2018 @ 9:17am 
Originally posted by Shakes:
- I figured out it was off because the top of the green channel for the eyeball normal was black while on Source Engine cornea textures the top was white. -
Okay... But how does it actually look within Source in a normally lit environment?

Originally posted by Shakes:
- it wasn't matching the reference image I was using. -Image- -
That specific reference image is wrong for Source. The outer ring has a dark red value on the left and a bright red value on the right, implying that the inside of the ring is "stepping out" of the texture, while it has a dark green value on the bottom and a bright green value on the top, which in Source's case implies that the inside of the ring is "stepping into" the texture. Invert the green channel of the reference image, and then it'll be correct for Source.
Shakes Oct 13, 2018 @ 9:18am 
Originally posted by Hunter in the Green Vest:
So, you're trying to make a normal map? This site might help you. It's not perfect, but it's okay.

http://cpetry.github.io/NormalMap-Online/
Errr, no. I have a normal map already. The problem is that it's missing the blue channel needed for the z-vector since the game it comes from calculates it in-shader using the green and red channels. Source only does this for one shader AFAIK and it's not the usual VertexLitGeneric.
Shakes Oct 13, 2018 @ 9:38am 
Originally posted by Zappy:
Okay... But how does it actually look within Source in a normally lit environment?
To be honest, I didn't really bother to check. I was using Valve cornea normals as references, and they had white on top of the green channel so I just went with that.

Originally posted by Zappy:
That specific reference image is wrong for Source. The outer ring has a dark red value on the left and a bright red value on the right, implying that the inside of the ring is "stepping out" of the texture, while it has a dark green value on the bottom and a bright green value on the top, which in Source's case implies that the inside of the ring is "stepping into" the texture. Invert the green channel of the reference image, and then it'll be correct for Source.
Whoops. I had that backwards the whole time?

But regardless, I just need a method that will get the last third of that image from the first two thirds. It's not the one that's missing the blue, obviously. I was just using it to try and figure a method that would get perfect results on another image.
Last edited by Shakes; Oct 13, 2018 @ 9:38am
Zappy Oct 13, 2018 @ 10:21am 
Originally posted by Shakes:
- I was using Valve cornea normals as references, and they had white on top of the green channel so I just went with that. -
I think that VALVe might have messed those up (again, some other programs think that a low green normal map value is down while Source thinks that it's up) and then not noticed that it's wrong because eyes are very small and such.

Either way, ignoring the blue colour channel, a black normal map colour in Source is up-left, and a yellow/white normal map colour in Source is down-right.

Originally posted by Shakes:
- But regardless, I just need a method that will get the last third of that image from the first two thirds. -
Again, it will usually be good enough with just a fully blue blue channel. If it doesn't look wrong in a 3D environment in Source, it's not worth the trouble trying to generate a "proper" blue channel from the red and green ones if you haven't done so in the past.
BlueFlytrap Oct 13, 2018 @ 7:50pm 
If you have the photoshop nvidia plugin the normalmap filter supplied with that has a normalize only tickbox. Fill the blue with white before applying that.

Then if you're real touchy about displaying right in source specifically go on and level the blue channel so the brightest value is 248.


Source does use the blue channel contrary to what a lot of people seem to believe.
BlueFlytrap Oct 13, 2018 @ 7:58pm 
If you don't have photoshop a temporary solution is to get the trial for crazybump and just import and export normalmaps from that. It normalizes on import by default. From there it's just a matter of stealing the blue channel from the newly exported one and placing it back in the original normalmap.

You don't want to keep the green and red from the crazybump export as it has a habit of altering their intensity.

The new blue channel is also darker by exactly 1 for everything so that makes leveling a little harder (or it would if not for the fact a quick paintover of two 1% opacity brush puts it nicely at 248).
Shakes Oct 13, 2018 @ 8:18pm 
Well I would try those methods except I don't have PS and CrazyBump doesn't even start for me (issues with WINE, apparently). In the meantime I'm using a close approximation and looking to see if ImageMagick would be able to do it properly.
Zappy Oct 13, 2018 @ 11:50pm 
Originally posted by BlueFlytrap:
- Then if you're real touchy about displaying right in source specifically go on and level the blue channel so the brightest value is 248. -
That's wrong. VALVe does not use ~97% blue as the most blue value in their normal maps. VTFEdit simply displays DXT-compressed textures' max blue brightness as 248, but the Source engine scales it up a little so that that does become 100% bright (255).

Source: Using VTFEdit to open a DXT5-formatted VTF texture, copying the image, pasting it, formatting it as BGR(A)888(8), comparing the two textures in an unshaded environment in Source, and noticing that the uncompressed texture is slightly darker in Source than the compressed one as the uncompressed one is not scaled up by ~3% while the compressed one is to balance out its max texture brightness to 100% display brightness.

TL;DR: Don't, because that's wrong.
Last edited by Zappy; Oct 13, 2018 @ 11:51pm
BlueFlytrap Oct 14, 2018 @ 2:05am 
Originally posted by Zappy:
Originally posted by BlueFlytrap:
- Then if you're real touchy about displaying right in source specifically go on and level the blue channel so the brightest value is 248. -
That's wrong. VALVe does not use ~97% blue as the most blue value in their normal maps. VTFEdit simply displays DXT-compressed textures' max blue brightness as 248, but the Source engine scales it up a little so that that does become 100% bright (255).

Source: Using VTFEdit to open a DXT5-formatted VTF texture, copying the image, pasting it, formatting it as BGR(A)888(8), comparing the two textures in an unshaded environment in Source, and noticing that the uncompressed texture is slightly darker in Source than the compressed one as the uncompressed one is not scaled up by ~3% while the compressed one is to balance out its max texture brightness to 100% display brightness.

TL;DR: Don't, because that's wrong.

The problem here is we're using something that did not come with a blue channel. It's inherently inaccurate. The leveling is something I do to help hide uv seams. Especially on mirrored boxes as source just hates having those flat.

Not that they'll display flat anyway since quake champions and source use different tangentspace (although converting between them is tedious. Especially since without the original blue channel you'll get some errors along the way) but it helps slighly and that's enough.


If this was something generated directly for source's tangentspace from something like handplane then yeah don't level that obviously.
Shakes Oct 14, 2018 @ 5:07am 
ImageMagick seems to be able to do the trick, though apparently my math is off somewhere since I can't get a blue channel that perfectly matches the reference image I linked to earlier.

If anyone is curious, the script looks like this:

convert input.png -channel B -fx "sqrt(1-((4*r^2-4*r+1)^2+(4*g^2-4*g+1)^2))" out/output.png

4*x^2-4*x+1 is the equivalent of using the curves tool in gimp to turn grays into black and the blacks & whites to white only (parabolic curve). Simply using r^2 and g^2 here doesn't work as that's the same as duplicating a layer over itself and changing the blend mode to multiply. The end result is very close but it's ever so slightly off. Here's[i.imgur.com] a comparison. The original blue channel is on the left, the one I get from the above expression is in the middle, and the right is the difference between the two.
< >
Showing 1-15 of 24 comments
Per page: 1530 50

Date Posted: Oct 13, 2018 @ 8:17am
Posts: 24