Warning: big wall-o-text and I’m ranting and venting. But there are some pictures near the end, so there’s that.
We solely rely on Unity at the studio I work for at the moment. Mainly because before Unreal Engine 4 was released, options were limited and Unity’s pricing and flexibility was pretty awesome. The landscape in 2015 has changed quite a lot since Epic launched Unreal Engine 4. It changed even more when Epic gloriously decided to get rid of the monthly $19 price point. Everyone and their dog had the opportunity to try out Unreal and the differences were pretty obvious. For me, one of the best things shipping with UE4 is the material editor.
I like to think of myself as a fairly technical guy. I won’t win any awards for best programmer ever any time soon, but I’d like to think that I can glance at a piece of code roughly figure out what it does. Shader language, on the other hand, is completely illogical and confusing to me. I just can’t read it and truly understand what’s going on. Unreal’s approach to visual coding with Blueprints is very nice. Whatever I’ve ever known about C++ has long since gone out the window, but at least I can hook things up in the Blueprint editor and get somewhere. The material editor is exactly the same thing.
A node based workflow makes so much sense in my eyes. Probably because I’ve grown up with Maya’s Hypershade and later on Substance Designer. To visually see data flowing from one node to another makes everything clear to me. The fact that Unity doesn’t have that, or has anything close to that, out of the box is completely infuriating. Shader Forge does a very good job filling that void. It’s not as complete as UE4’s material editor, but at least it robust enough to do some cool shit. I like that.
Now, if you happen to have been earshot of me last week, you might have picked up on an extremely long rant about Unity’s PBR workflow. The short version is that I don’t really like it.
Rewind until before Unity 5 was released. When they showed their PBR examples I was pretty stoked. The masked lady looked great and it seemed they were on the right track. When I later discovered they had adopted the specular/gloss workflow, instead of metallic/roughness I was kind of confused. S/G works just as fine as M/R, but the journey to get to a result is so much more convoluted. So rejoice when they later also included a metal/roughness workflow.
Instead they didn’t.
They opted for a metal/smoothness workflow. I’m sure they had some pretty good reasons for doing so. Because if they didn’t, I would have made no sense at all. I am nowhere near an expert on shaders, so I’m just going to assume they had a solid reason. And who knows, maybe they’ll even convince the rest of the world to invert their roughness maps and we’ll all have a smoothness party on the beach. Who knows?
On to the next part that irked me… the way they break up their textures. The albedo map uses RGB for color (great!) and A for transparency. Good enough in my book. The metallic map uses the red channel for metal and the alpha for smoothness. Which is a fairly good start, except for the fact that they’re not using the green and blue channel. There’s a still a height map and ambient occlusion map to be added, both fit nicely into those two channels. But Unity’s standard shader wants a greyscale texture for height and ambient occlusion. It will happily accept RGB textures as well by the way. That’s just weird.
And again, I’m not saying that anyone is going to end roasting in hell for this, but for me personally it feels awkward. The shader uses at least 4 greyscale textures, stuff those 4 textures in 1 image file and save some room in your project.
So I raised my head above my monitor and asked the guy sitting in front of me if he could take a look at my wishlist and see if he couldn’t make it work. He’s pretty great with shaders, he wrote a crapload of them for a children’s game we made a couple of years ago. Well he did look at it, but couldn’t really figure out what was going on. And he’s one smart sashimi, so if he says he can’t figure something out, I usually get worried.
To sashimi man’s left sits another programmer, let’s call him The Captain. He was nice enough to show me Shader Forge and hot diggity! This was the heroin shot I was looking for. So I took it upon myself to see if I could use Shader Forge to come up with a better standard shader. There are still some bugs to work out, but everything seems to be working pretty OK for the most part.
The first thing I started working on was a way to get 4 grayscale maps into 1 file. Sure, you can slap everything together in Photoshop, but that just takes too long. I first thought about writing something in C#, but then moved on to Python for 2 reasons: cross platform and the Python Imaging Library. PIL is extremely easy to use, yet extremely powerful. And it also had the two things I was looking for built in: splitting up an image in different channels and combining different channels into 1 image. You can grab the script here: image_packer. It relies on the Python Imaging Library, which you can find here: http://www.pythonware.com/products/pil/. Pillow is a fork of PIL, also worth checking out. The program itself is easy to use. Just select the maps from your hard drive, click the Combine Maps button and it will write out a combined texture file. The way it’s set up is like this: Red: roughness (not smoothness!) Green: metallic Blue: ambient occlusion Alpha: height
EDIT: I made a better version of this tool, check it out here: http://nielsvaes.be/misc/channel-scrambler/
With that working, I turned to Shader Forge to… well… forge a shader. Kinda made me feel like Eorlund Gray-Mane, sans the ribbed body. I got it to work just the way that I liked it I think. They only thing that’s missing is the height map. Which, for whatever reason, is not included in the Shader Forge Main node. Weird, but something that can be worked around for the time being.
The final shading network is really not that complicated. It gets a little wiry at the bottom when splitting up the different maps for the packed rough/metal/ao/height texture. But other than that, pretty clean, pretty quick and in my opinion, better than Unity’s standard shader.
As quick comparison, here’s an object The Captain made with the standard shader:
And the same object with my shader:
There’s some difference between the two, but I’m pretty happy with the end result. You can grab the shader here: nielsStandardShader
I’ll probably end up changing some stuff, but feel free to play around with it yourself and use it in any way you like.