About Me

Hi. I'm Josh Ols. Lead Graphics Developer for RUST LTD.

Contact:
crunchy.bytes.blog[at]gmail[dot]com

View Joshua Ols's profile on LinkedIn

Meta
Monday
Jul202015

Unity shader code injection

Background

I’m sure many of you have had this thought, “Boy, Alloy is great but I really wish I could use its area lights and nice attenuation with other Unity shaders!” As luck would have it, I am implementing that very ability using a little known feature of Unity’s shader system.

“What might that feature be?”, I hear you ask. Well…

 

Shader code injection

That’s right, it is possible to force a shader to use custom code without having to modify the shader. It works because of how Unity’s shader system resolves “#include” directives for handling headers. If you use it like “#include “myHeader.cginc” with just the filename and no path then Unity will first look in the same directory as the shader file, and then search the editor’s hidden include directory.

So basically, if the shader uses an editor header (eg. “UnityCG.cginc”) you can trick that shader into using a customized local version by copying the header into the same directory as the shader. Then you can simply modify that local copy and the shader will use that instead. This will work for any shader code, even the Standard shader and the generated output of a Unity surface shader.

 

Conclusion

After mucking around in surface shader output, and mangling some of Unity’s macros, I now have it working with Alloy. So any surface shaders that use the Standard and StandardSpecular lighting models can now be lit using Alloy’s lighting model and material options. This way, forward-only shaders can now match deferred-compatible shaders when used alongside Alloy.

Keep an eye out, as we will be releasing this new feature in beta form in our next bugfix release.

 

Friday
May082015

Unity, Consoles, Shaders...

UPDATE2

Also it seems like it would be a good idea to test in OpenGL mode on an AMD/Intel GPU. Their GLSL compilers seem especially strict, so you're likely to catch the most bugs on those platforms.

 

UPDATE

Apparently you also need to assume some of the strict assignment and swizzling rules of GLSL. Certain things like accidentally assigning a four component vector input to a three component variable will pass for Direct3D, but blow up once you hit OpenGL.

 

ORIGINAL

To all the people authoring shaders for Unity with the goal of having them work on all of Unity's supported platforms I say the following:

  1. The shaders are HLSL! Unity still calls them Cg for legacy reasons, but they've all but abandoned that platform. So when looking up how to do something in the shaders, look for HLSL tips!
  2. Read the header "HLSLSupport.cginc" and burn its contents into your memory. Inside you will find all the crazy preprocessor juggling that Unity has to do to hide platform and language API differences. 

Today I learned this lesson the hard way with Alloy's parallax occlusion mapping code. Basically, in Cg if you want to apply texture coordinate derivatives then you use an overloaded version of tex2D(). In HLSL, you have to use an explicit intrinsic called tex2Dgrad().

Guess which one is platform-safe in Unity's shaders? To find the answer, follow tip #2. :p

Wednesday
May062015

Alloy 3.1 - Asset Store Madness

It's that time again! The Unity Asset Store is having another madness sale. For this week only, you can pick up Alloy 3.1 half off at just $62.50! If you've been hesitating due to price, now's the time to jump in and grab it! XD

Alloy - Unity Asset Store

Sunday
May032015

TeamColor refinements 

Background

Not long ago, I realized that Alloy's Team Color formulation was grossly inflexible and not super intuitive. The primary reason being that if you had more than one color mask overlapping a given pixel, the top one would dominate. This presented a problem if you wanted to mix multiple colors and precisely control their contributions to the final combined color.

It looked a little something like this:

half3 tint = lerp(half3(1,1,1), aTint, masks.a);
tint = lerp(tint, rTint, masks.r);
tint = lerp(tint, gTint, masks.g);
baseColor *= lerp(tint, bTint, masks.b);

 

As you can see, it was layer order-dependent and made it basically impossible to have all the colors influence one fragment in a clean way. The only problem it really solved was avoiding black zones between masked colors by ensuring that the starting color was white.

 

New Approach

I needed something that would still prevent the black zone problem, but also allow easy blending between all the masked colors covering a given pixel. Plus, when the total weight of the masks is above 1, it needs to renormalize all the mask weights so that they total 1. Finally, when the total weight is below 1, it needs to add the color white by the remaining weight. 

So I switched to something more like this:

half weight = dot(masks, half4(1,1,1,1));
masks /= max(1, weight);
baseColor *= rTint * masks.r 
            + gTint * masks.g 
            + bTint * masks.b 
            + aTint * masks.a 
            + (1 - min(1, weight)).rrr;

 

This new approach does everything I need without being overly complex and/or costly.

Sunday
Mar012015

Alloy 3 is up on the asset store!

After months and months of work it is finally here! See the Unity forum thread for details:

Alloy - Unity Asset Store