Hello, I know very little about shaders, so there’s that. What I’m trying to do is have each triangle cycle / lerp between two colors. In this case, let’s say blue and white. I want to have each triangle be at a different part of the lerp compared to other triangles.
I’ve managed to get the whole mesh to let between the colors, but I can’t figure out how to make the triangle or even the vert position have much of an effect on when it changes.
I’ll provide my vert and frag, hopefully that’s helpful. I don’t know where to look, or at least I don’t know the words to look with. Google results have sent me to a bunch of no-answer topics, unfortunately.
Anyway, any help would be greatly appreciated. Even if it’s a simple “nah man, you can’t do that”
In order to do this on a per-triangle basis simply, you’d need to have vertices unique to each triangle - for instance, you’ve faced-mapped your object so that the verts of each triangle have their own set of texture co-ordinates. So in terms of a “simple” solution, per-triangle requires a bit of set up.
Per-vertex, however, all you’d need is something different about each vertex to pipe into the fragment shader to control the lerp. This could be the texture-coordinates, the vertex positions in object-space, the normals - anything will do, really, but which you choose will visually impact the way each vertex inherits its lerp value.
So, for instance, let’s use the vertex positions in object space:
Something like that. “Frac” just returns the decimal part of whatever’s in there, it just means you end up with a nice neat zero-to-(very close to)one value which you can then operate further on if you wanted - you don’t really need it in your case since your abs(sin(…)) already does that though. The " * PI * 2" is just to ensure the delta value encompasses the whole range of Sin() value for maximum spread of values - you’ll need to define PI as 3,14159265 somewhere, it’s probably defined in one of the Unity includes but I couldn’t say which one off-hand.
Also I’ve just made up some simplistic way of turning the vert positions into the delta - you can get creative with precisely how you combine those values, I’m just really giving you a jumping off point and some ideas to be getting on with
The mesh is being generated from a level editor tool I’ve created, so modifying the uvs shouldn’t be too much of a change. If I’m understanding you correctly, I should be able to modify the uv generation pretty easily to be 0-1 for each triangle instead of treating the whole mesh as a giant rectangle like I am now
I’ll give both a shot, starting with your suggested changes, since they seem the simplest.
Oh I should probably have noted that doing it the way I’ve posted will mean you won’t get nice neat “this vertex is this colour, that vertex is that colour, with a nice linear blend in-between”, you’ll get colour variation over the entire model. To have your colours per-vertex in the literal sense, move the delta and lerp stuff into the vertex shader part, change the “objSpacePosition” in the v2f struct to be “colour”, output the lerped colour to o.colour, and then just do
Which produces a pleasing effect, so thats great. The only problem now is that, for some reason, the color lerp sometimes returns a magenta, even though _Color and _SpecColor are blue and white respectively. I can’t think of a number that I could put where sin is that would produce a magenta color
Well sin will produce negative values - really you ought to take the result and do * 0.5 + 0.5 (or use abs as you had before) to push it between 0 and 1. But pushing the lerp value below zero or above 1 I think should not behave in a way beyond a stronger blue or a brighter white. But perhaps this depends on the platform / graphics card / whatever.
Yeah, definitely. I went in and just hard coded a negative value, an in range value, and a value above one and all of them produced a color that I expected. For some reason, doing the calculation causes some of the triangles to be magenta. It is telling (maybe?) that its always magenta and not any other color. It makes it feel like there is some other strange thing happening that is causing it to error out in some way.
… he said, knowing very little
If it’s not the out of range values, then the only thing I can see - I mean there’s nothing else - is the PrimID. If that occasionally throws a wobbly then, well, I don’t know what would happen - nor whether it’s possible for that matter - given it’s not something I’ve ever needed to use personally. But I also can’t help but notice that magenta is also the “this shader threw errors” colour, although I’ve never known that happen on an individual triangle - I assumed it was an all-or-nothing thing. So I’m sorry, but I can’t be of any use at this point
(Assuming you’ve not changed the vert shader in any way that is)
A few notes:
_Time is a float4. That code might result in it using _Time.x (which is time / 20) or using a different time for each component (time / 20, time, and time * 2) depending on the compiler.
As already noted sin() is a -1 to 1 range. Negative values aren’t guaranteed to lerp the same on all hardware, and hard coded values don’t necessarily produce the same results as those passed to the shader from a material or calculated within it.
The randPerPrim might sometimes be zero, or very, very close to zero. In that case the _Time.x * randPerPrim would be zero and sin(_Time.x * randPerPrim) will also be zero all of the time. Maybe try _Time.x + randPerPrim or _Time.x + (1.0 + randPerPrim)
None of those would cause the occasional magenta you’re seeing though. Usually that’s the “this shader failed to compile” color, not sometime that would happen on random parts of a shader.
To respond to this question, it should be consistent for any meshes that are static batches, instances, or not batched. With dynamic batching it is not guaranteed to stay consistent between frames. If your objects are getting dynamically batched and this is causing an issue you may need to add Tags { “DisableBatching”=“True” }
Thanks for that bgolus - not something I’ve ever needed to use but nice to know it’s a thing which exists pops in brain for future reference. Also nice catch with the _Time.x thing - I’m not particularly familiar with unity so when I read things like _Time my brain assumes it’s a uniform passed in manually in the manner used
Ok, so it turns out not to be the “help, I’m broken” color in this case. If I do all of my math calculations on their own line, and pass the result in to the lerp, the magenta goes away.
Interestingly, If I do that, _Time stops working and I have to use _Time.y to make it animate again. I guess there’s some sort of strange happening when you pass a float4 into lerp and it was just adding the red in. Who knows.
Hi bgolus - sorry to be a pain, but I’m struggling to work out how that hash function helps. The first line will surely always produce a float3 with three identical values, so the dot product will always return a predictable value so by the final line you’ve still got three identical terms - so the whole thing doesn’t really achieve a lot for all those manipulations other than slightly shift the incoming values. At least that I can see. I mean, it’s late here in the UK so I might just be being dense
This is the random value gen I always use:
float GetRandomValue(float val)
{
float f = val * 37.0;
return frac(sin(f)*104003.9);
}
edit: Yeah, I just tested it by piping the vertex x position in and returning the resulting hash code as a grey scale and I just get rather regular bands rather than the noise I get with mine. I assume there’s probably been a copy&paste error somewhere along the line in the code you’ve included.
Added screenshot comparison of the two functions using the interpolated vertex x as input:
It’s a direct translation of a function from a shadertoy… And yeah looking at it closer it’s not a great one.
I tend to avoid sine based noise functions these days as they’re more expensive and not necessarily better if using the right numbers. There’s one on shader toy it always takes me a while to track down that’s really good.
Hadn’t seen this one before, it looks like its quite good. I’m a little sad it’s sine based though …
One other comment about that noise function I used above, it’s unfair to compare it to your noise function as it wasn’t written to work with small float ranges. The interpolated vertex position is going to be inside of a 0.0 to 1.0 range, and that noise function is much larger steps. Compare your sine based noise to vertex.x * 1000.0 for a better comparison. Especially test it using vertex.x + _Time.w and see how long the sine based function lasts before obvious streaking or patterns show up.
Yeah, fair enough although as a general purpose noise function I think something that works best in the 0…1 range is more useful than something which works worst under those conditions. But if nothing else all the flaws with each are worth discussing to demonstrate how even something as simple as a noise function needs to be understood so that you get the results from it that you expect. So personally, I’d rather use a simple function I can easily comprehend.
I know what the sine wave looks like, so I can predict the sort of results I’ll get easily. I know that if my number increments by more than the frequency of the wave, I could start getting bands, etc. Some of these noise functions, though, with crazy magic numbers - I find them almost impossible to visualise and easily determine when or how they might start failing. So I’ll take a marginally slower sine function than the alternative - that one function is unlikely to be my bottleneck