Hello, Im trying to create a shader that rotates arrows depending on certain vector3 position. Image 2 shows kinda what im going for, but theyre using arrow prefabs which is not scalable since currently theres 2500 spawned in depending on the plane size. If you can see, the arrows are pointing inward towards the sinking hole area, problem im facing is currently all the arrows rotate on the shader, but each arrow needs to have a certain rotation value each to make that look of all the arrows pointing in that direction. Any advice on how to go about it? Ive seen things like computeBuffer and hlsl but i havent dwelled into any of these and Im curious if this functionality is even possibly with just shader nodes. Ideally if I have 2 positions on seperate ends of the plane, arrows closer to those positions would be pointing in their direction, while those closer to the middle (far away from the vector3s), will have less rotation applied to them .
#Arrow Shader Rotation
1 messages · Page 1 of 1 (latest)
i guess the part thats confusing me right now specifically for the next step is the rotation of all arrows are controlled via 1 vector3 property, but I dont think making a ton of arrows each with their own property to control each arrows rotation is the answer. There has to be a more autonomized way of doing so, and I believe u cant have arrays in shader graphs anyway
Could manipulate uvs & calculate rotation with atan2 like this :
To explain a bit,
- Uses
Fraction&Floornodes to split UV space up into separate cells Floor + (0.5, 0.5)to calculate center position of each cellSubtractnode gives vector from the cell centers to the position arrows should point atArctangent2to calculate signed angle forRotatenode
Pointing fingers are a fantastic texture choice! haha
Haha yeah, best texture I could find on the dropdown that wasn't transparent and couldn't be bothered to make my own texture
solid, precisely what I was looking for in terms of guidance. TY!! Im fairly new to shader graphs, so ill be looking more into what all those extra nodes you used to achieve this effect do specifically, since the effect IM going for can have multiple points to look at, so ill have to figure out a way to have arrows gradually point towards those plotpoints as they get closer but point towards other plot points if they're closer to that specific "seperate cell"
Note that if you want "real" arrow objects, like billboards, VFXGraph might be a better choice to do it.
from a quick search, looks like billboards are 2d objects in the world space forced to look at the camera to make sure it's always visible despite it existing in a 3d space? If thats the case, no im not looking for "real" arrows, their rotation should be determined by the 3d space/ grid.
Although currently ran into a problem where if I attach my grid script which deforms the mesh to make the valleys/hills I need, but the shader seems to disappear. There's nothing in the script shader related itself, but im assuming the fact im recalculating normals and tangents, something regarding the UVs gets messed up, but even removing the recalculating mesh functions applies the same effect.
I was hoping to test out if at least 1 position point would work for now, but since they go invisible, that doesnt seem to be the case : /
unformed mesh for reference (choppy af but its ok for now lol)
It's very possible that the mesh after deformations is no more in its original bounds and gets culled.
You can use this function after deforming to fix it.
sadly that doesnt seem to work. I had tried using that function alrdy when I stumbled upon RecalculateTangents(), and kinda threw in bounds() as well, but the result is the same, a completely white plane.
void AssignMesh()
{
mesh.Clear(); // clears previous data from the mesh
// updates mesh data
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals(); // updates normals to reflect the new data
mesh.RecalculateTangents();
mesh.RecalculateBounds();
}
void DeformMesh()
{
if (chargePositions.Count != chargeValues.Count) return; // Safety check
for (int v = 0; v < modifiedVertices.Count; v++)
{
Vector3 vertex = modifiedVertices[v];
float totalPotential = 0f;
Vector3 distance = new Vector3();
// Superposition of potentials from all charges
for (int i = 0; i < chargePositions.Count; i++)
{
Vector3 localChargePos = transform.InverseTransformPoint(chargePositions[i]);
distance = vertex - localChargePos;
float r = distance.magnitude;
float potential = k * chargeValues[i] / Mathf.Max(r, 0.1f); // Coulomb potential
totalPotential += potential;
}
// Height based on total potential with smooth falloff
float height = Mathf.Sign(totalPotential) * Mathf.Log(1f + Mathf.Abs(totalPotential) + 1e-10f);
float falloff = Mathf.Exp(-distance.magnitude * distance.magnitude / (planeSize.x * planeSize.x)); // Gaussian falloff
modifiedVertices[v] = new Vector3(vertex.x, height * falloff, vertex.z);
}
mesh.vertices = modifiedVertices.ToArray();
mesh.RecalculateNormals();
mesh.RecalculateTangents();
mesh.RecalculateBounds();
//AssignMesh();
GetComponent<MeshCollider>().sharedMesh = mesh;
}
so the main issue is the shader not being "read" properly though right? And the result is not the arrows but some sort of default value unity sets that results in the white color of the plane, almost disregarding my shader ?
cuz ive been playing around with different values here to understand a little more about what they do, but regardless the result is the same, either completely white or transparent, which im assuming is that culled thing u mentioned
if I keep the the chargePOS vector2 at 0.5, 0.5, the plane remains white but going lower or heigher from those float values, thats when it becomes transparent. So i cant tell if this is on my shader graph or my deforming script
How is mesh set/initialised? You'd likely want it set to GetComponent<MeshFilter>().sharedMesh (or a clone) to retain the original UV data. And don't call mesh.Clear()
void Awake()
{
mesh = new Mesh();
meshFilter = GetComponent<MeshFilter>();
meshFilter.mesh = mesh;
GeneratePlane();
//arrowMaterial = arrowPrefab.GetComponent<MeshRenderer>().sharedMaterial;
//tileCount = (int)arrowMaterial.GetFloat("Tiling");
}
whats the difference between .mesh and .sharedmesh (and I guess MeshFilter? ) ? And how come I shouldnt be using clear()? id think clearing it before rewriting it would be a positive ya ?
.mesh creates a copy for you, while accessing .sharedMesh is the asset (unless overridden by the script)
https://docs.unity3d.com/ScriptReference/MeshFilter-mesh.html
Typically if other objects use the same mesh and you want the script to only affect one, you use .mesh. But if you're creating (new Mesh()) and assigning your own that works too.
The issue with just new Mesh(); is it doesn't contain any data until you code it. You've added vertices but there's no UV data which is how the shader maps the texture.
You'd either need to code that. Or assuming the original plane mesh and procedurally created one still has the same vertex count/order, you can just use that original data and only modify the vertices
meshFilter = GetComponent<MeshFilter>();
mesh = new Mesh(meshFilter.sharedMesh);
meshFilter.sharedMesh = mesh;
...
void OnDestroy(){
Destroy(mesh); // ideally also clean up when the mesh isn't needed anymore
}
(And since using the original data is now the intention, that's why you wouldn't want to .Clear() it)
An alternative is changing the shader to map the texture differently, you could use a planar mapping (Position node -> Swizzle "xz"). That way it doesn't use/need any UV data. But kinda a weird workaround.