Hey all, I’m trying to replace white with a material specific color, and do so in such a way that lighting still applies. To that end, I downloaded the Sprite/Diffuse source and went from there. Here’s what I have:
Thanks, @jvo3dc ! I’m not sure it’s fair to say that’s too specific, though. We want a one-for-one replace. It fits the art style.
Now that the color change is being applied however, it would seem as though lighting is still not factoring in at all. Any ideas as to why that might be?
Using a specific color will work as long as you are using point filtering on your texture, and no anti-aliasing of an kind. The second you start blending colors at all you will get ugly white fringes around your color blocks. It should work for you as long as you are sticking with a very crisp, pixelated look.
If you do run into blending problems, I would use a separate 8-bit mask texture to apple a color tint. Just multiple the color by the mask and the texture. It’s a bit of extra memory, but it’s more robust than trying to do a color replacement based solely on the RGB values.
Your lighting should still work. That is done by the surface shader after your surf function. Just double check that you aren’t accidentally putting anything crazy into the output structure (like a huge value into the emissive channel or something).
@CaptainScience_1 I am indeed using point filtering without anti-aliasing. Pixel perfect Unity 5 ftw…
To reply to both of you, lighting is in fact being applied to the texture, but not at all the the sections with the color replaced. They (being the color-replaced pixels) for some reason seem to not receive the lighting values at all, though I believe I coded it in such a way that they should. Here’s the code I’m using at this point:
The only obvious problem I see is that you are multiplying in the vertex color (IN.color) before you are doing your replace. That means it will be messing up the selection of pixels to replace and will be overwriting the IN.color completely when the replacement happens.
Also, if you are using exact pixel values, then you can get rid of the distance function which is expensive especially on older hardware where it will be compiled to multiple shader instructions.
Something like this should work, and uses a couple fewer instructions:
void surf (Input IN, inout SurfaceOutput o)
{
float threshold = 2.985; // Threshold of 0.995 per channel (just under 255/256); added three times, once for each color channel
float4 result = tex2D(_MainTex, IN.uv_MainTex);
float luminance = dot(result.rgb, float3(1,1,1)); // Add up the color channels in the texture; dot products are fast
float factor = step(threshold, luminance); // Step instead of distance is a bit faster; selects all white pixels
result.rgb = lerp(result.rgb, _Color, factor);
o.Albedo = result.rgb * IN.color * result.a; // Multiply the vertex color *after* the replacement is done
o.Alpha = result.a;
}
You can try that, but I’m still not sure why the lighting wouldn’t be working. The vertex color you were losing should just be the color coming from the mesh unless I am mistaken. The lighting should all be happening after the surf() function is called so you shouldn’t be able to mess it up from there even if you wanted to.
In the source image, the center trim is obviously perfect white. The red shows no change from the lighting, while the rest of the image clearly does. @_@
I can’t think of why that is happening. I dropped that shader into a basic scene and it seems to be working fine with my test image:
This is a 3D scene, but I’m not sure what would be different about the lighting calculations in a 2D project. If you want to upload a small test project somewhere that reproduces the issue, I can maybe take a quick look and see if I (or someone else) can isolate the problem. There could be some obscure setting somewhere that is affecting things in a strange way.
I couldn’t replicate the issue in that project either. I just created a new scene with a sprite and a point light. It seems to work with the distance shader that was there, and the step version I posted.
It might be some kind of Unity cache issue where it is still using an older version of the shader, or you have the wrong material assigned accidentally. You can try creating a new shader and material from scratch and copy/paste the code into it to be sure it is using the right code.
Other than that just experiment and poke around a bit. I can’t replicate the issue on my end so I don’t have any good guesses as to what’s causing it.
Oh, actually, I know what it is. It’s because you’re using a completely red color. The values of the blue and green channel are 0 so no amount of light will ever brighten them up at all.
That’s because the directional light is already bright enough to already completely saturate the red to (255,0,0). Adding more light can’t brighten it at all. With even small values in the blue/green channels it will brighten up a bit, but it will take quite a bit of light to get it all the way to white.
Edit: Change your directional light intensity from 0 to 8 using full red (255,0,0) versus mostly red (255, 25, 25) to see the difference.
That makes sense, with the lighting being multiplicative… Thanks for all of your help.
As an aside, why does the color picker in the unity interface work off of HSV with no way to switch to RGB? Gah.