Why is Shader Graph Transform node affected by UV mapping? - unity3d

I've had an issue with normal maps not behaving correctly in my custom shader and finally managed to find the cause. It turns out it was the way UV of my objects were mapped. In UV0 I stored a mapping to a color palette texture - the UVs were all scrambled together as the only thing that mattered was that they are on a pixel with the correct color. In UV1 I stored the traditional UV uwrap, which I used to apply the normal map. To get the normal map I used a set-up like this:
I'm doing my own light calculations so I need to transform the normal from tangent space to world space before using it.
This approach was causing two issues - weird artifacts and the normals being "stuck" to the object:
The sphere on the right is upside down and if you look at the normals they are also upside down. The artifacts are on both spheres, but they are visible on the right one from this perspective.
What seems to be the cause is the way I used UV0 to map the object to a color palette. It somehow affects the tangent to world space transformation done by the Transform node (I know it's this node because removing it makes the artifacts disappear). Also, swapping the UV channels so that the traditional unwrap is in UV0 and the palette mapping is in UV1 fixes the issue:
There are no artifacts and the normals aren't stuck to the object.
So why is the transform node affected by UV mapping? I thought it does the transformation based on the geometry of the object. And if it uses UV maps, why is there no dropdown to select which UV it's going to use?

The tangent space (aka texture space) is partially defined by the geometry (the normal) but also by the uv coordinates (the tangent).
Just as the normal is derived from the vertex position, the tangent is inferred from the UVs. It's essentially an object space vector (xyz) that points to the U axis (horizontally in your UV space) and is perpendicular to the normal.
The normal map is a vector in texture space, and the channels of the bitmap can be seen as the offsets from the vertex defined tangent space for each of its vectors.
half3 TangentToObjectSpace(half3 input, half3 nml, half3 tgt, half3 btg) {
return tgt * input.x + btg * input.y + nml * input.z;
}
As you can see the input.x (normal map red channel, which defines the horizontal part of the vector) is modulating the tangent. We need another vector for the green channel, which we can generate using a cross product: provided the two existing vectors you get a new one perpendicular to both. The catch: for flipped UV shells this generated vector is pointing in the wrong direction. In Unity tangents are actually vector4, and the last component (w) is the flip of the binormal.
Unity uses the tangent space of the first UV coordinate by default, which is why you fixed your issue by swapping them :)

It's very, very likely that the Transform node is dependent on the UV selection insofar it depends on the output of the Texture Sample node. So it`s probably a subtle difference of the output in the sample node that makes the transform node behave this way, and not some hidden UV setting on the transform node.

Related

Hide parts of mesh overlapping another mesh in Unity

I have thoses two meshes:
In my game, I put the hat on the hair at runtime:
As you can see, as expected, the hair is visible outise the hat part.
How can I achieve this in Unity (what kind of mask shader should I use?):
I've tryed to make a depth mask but it hides every meshes in my scene. I just want to hide the hair, not others meshes.
And what if I have two player having the same case? Would player mask hide player 2 hair? How can I avoid that?
What I would do:
write a C# code that gets the pivot position (bottom part of the hat) and its up vector every frame.
build a plane with these values. The up vector would be the normal vector of the plane and a plane can be defined by a point and a normal vector.
I would pass the equation of the plane to the shader (via Material.SetFloat or Material.SetVector) and evaluate if the world positions of the hair vertices are in the correct or in the wrong side of the plane.

Cull off parts above the mesh

So, I want to make scene same to this Sphere Scene
Now I have mesh with random generation as a ground and a sphere. But I dont't know how to cull off spheres geometry above mesh. Tried to use Stencil, and hightmap. Stencil rendered ground in front, but sphere above ground is still rendered. Using heightmap, to get know if it needs to render (I compared height map and worldPos) is problematic, because the texture is superimposed over the all sphere, and not projected onto it. Can you help. Is there any shader function to cull off all above mesh.
I did something similar for an Asteroids demo a few years ago. Whenever an asteroid was hit, I used a height map - really, just a noise map - to offset half of the vertices on the asteroid model to give it a broken-in-half look. For the other half, I just duplicated the asteroid model and offset the other half using the same noise map. The effect is that the two "halves" matched perfectly.
Here's what I'd try:
Your sphere model should be a complete sphere.
You'll need a height map for the terrain.
In your sphere's vertex shader, for any vertex north of the equator:
Sample the height map.
Set the vertex's Y coordinate to the height from the height map. This will effectively flatten the top of the sphere, and then offset it based on your height map. You will likely have to scale the height value here to get something rational.
Transform the new x,y,z as usual.
Note that you are not texturing the sphere. You're modifying the geometry. This needs to happen in the geometry part of the pipeline, not in the fragment shader.
The other thing you'll need to consider is how to add the debris - rocks, etc. - so that it matches the geometry offset on the sphere. Since you've got a height map, that should be straightforward.
To start with, I'd just get your vertex shader to flatten the top half of the sphere. Once that works, add in the height map.
For this to look convincing, you'll need a fairly high-resolution sphere and height map. To cut down on geometry, you could use a plane for the terrain and a hemisphere for the bottom part. Just discard any fragment for the plane that is not within the spherical volume you're interested in. (You could also use a circular "plane" rather than a rectangular plane, but getting the vertices to line up with the sphere and filling in holes at the border can be tricky.)
As I realised, there's no standard way to cull it without artifacts. The only way it can be done is using raymarching rendering.

Cross section shader for box bounding using amplify Shader

I am trying to create shader through amplify shader for a cube to cut through plane or any mesh when cross section. I know that I should be using size, rotation and position for that but what exactly to do with them that I don't know. Yup by that it means that I am new to amplify shader and also in shader programming so please don't provide shader code as I need to make it customizable for future so please help me out in amplify shader nodes.
Currently I have this effect but I want to make it more box bounding specific not plane normals based.
I want not this effect but the box effect shown below. This was achieved through ray marching concept but this I want to achieve with Amplify Shader. Kindly guide me through this.
This is what I have done so far with the amplify nodes
Result:
Here is the result of doing the shader using "Amplify Shader":
Solution:
First we'll call the green cube the "intersector" and the red cube the "intersectee".
So as you've done with the plane, the cutout works because the back face of the intersector is shown when inside the intersectee and the intersectee front face is show when it is inside the intersector.
Create a shader (which is used by both cubes) and put them into two seperate materials - apply individual materials to each cube. After this we can get into the actually shader node stuff.
First we need to make sure "Cull Mode" is off (Output Node > Cull Mode > off). This will ensure the back face is actually rendered (This can be optimized by decided depending on where the cube is in the intersector).
Next we need to get the surface point in object space:
Most of the variables will be defined in script. The rotation matrix is used to rotate a point. However, it is inversed as the rotation matrix rotates the cube into world space, therefore, inversing this would rotate the world space point into object space. We also get a "_Cubepos" which is the position of the cube to intersect with (E.g it would be the intersector if shader is on the intersectee). This is subracted by the world pos as the rotation matrix rotates around the origin. After this it is added back to be in the correct position.
This leads to the next section where the extents are added and subtracted to the "_Cubepos" and "_CubeExtent" to find the minimum and maximum extents.
Unfortunately, Amplify shader has no good way to check if a vector lies within two vectors. So we have to break it into components. (I encourage you to learn how to write shaders). Each compare with range returns 1 if the point in object space is within the extents for each axis. If one returns 0 we use the last multiply node to make sure the final output will be 0.
Finally, we get to the last part of the shader. The "IsIntersector" is set in script to be 1 or 0 depending on whether the cube we are refering to is used to intersect or is an intersectee. Depending on the scenario, here we set the opacity mask to 1 or 0.
After this we have to define the script to attach to each object. Add a new script and type the following in:
[ExecuteInEditMode]
public class SetVar : MonoBehaviour
{
//Transform of opposite cube
public Transform intersectingCube;
//Is this an intersector or intersectee
public bool isIntersector;
//Material of object
public Material mat;
// Start is called before the first frame update
void Start()
{
//Get material
mat = GetComponent<Renderer>().material;
}
// Update is called once per frame
void OnRenderObject()
{
//Calculate rotation matrix
Matrix4x4 m = Matrix4x4.TRS(-intersectingCube.position, intersectingCube.rotation, Vector3.one);
//Set shader variables
mat.SetMatrix("RotationMatrix", m);
mat.SetVector("_Cubepos", intersectingCube.position);
mat.SetVector("_CubeExtent", intersectingCube.localScale / 2.0f);
mat.SetFloat("_IsIntersector", (isIntersector) ? 0 : 1);
}
}
Then we can set the correct inspector values depending if the cube is an intersector or intersectee. Here is an example for the intersector cube:
Make sure to have the IsIntersector ticked depending if the cube is an intersector or not.
Here is a link to the shader: http://paste.amplify.pt/view/raw/4b248bc3. Also to do this for any mesh is a very complicated operation - too complicated for nodes. Learn about shader code and use a raycasting algorithm to determine if the point is inside the cube.
Also, alternatively for any convex shape. You could calculate each plane and then using your method already used, can check if the world position point works for every plane. For a cube there would be 6 planes, however, its a bit slower than the above method (as it is optimized for a cube).

Unity Repeat UV Coordinates On Quadtree Shaderforge

As u can see in the image, on larger tiles (n > 1) the texture should be repeated as long as the current rect size.. i don't know how i can achieve this!
FYI, im getting the tile texture id with the alpha value of the vertex color.
Here the shader im using..
[UPDATE]
Thanks for clarifying the uv coordinates, unfortunately that doesn't answer my question. Take a look at the following pixture...
Your shader is fine; it's actually the vertex UVs that are the problem:
So for all rectangles the uv coordinates are as following [0, 0] / [0, rect.height] / [rect.width, 0] / [rect.width, rect.height]. So the uvs are going beyond 1
Your shader is designed to support the standard UV space, in which case you should replace rect.width and rect.height with 1.
By using UV coords greater than one, you're effectively asking for texels outside of the specified texture. When used with a texture atlas, that means you're asking for texels outside of the specified tile -- in this case, those happen to be white, and that's what you're seeing in the rendered output.
Tiling with an atlas texture
Updating because I missed an important detail: you want a tiling material.
Usually, UVs interpolate linearly:
For tiling, you essentially want more of a "sawtooth" output:
For a non-atlas texture, you can adjust material scale/wrap settings and call it done. For an atlas texture, it's possible but you'll end up with a shader and/or geometry that aren't quite standard.
The "most standard" solution would be if your larger quads are on a separate mesh from the smaller ones:
Add a float material param named uv_scale or some such
Add a Multiply node that scales incoming UVs by uv_scale
Pass output from that into a Frac node
Pass output from that into the UV Tile node
Pseudocode is roughly: uv = frac(uv * uv_scale)
If you need all of your quads to be in the same mesh, you end up needing non-standard geometry:
Change your UVs again (going back to rect.width and rect.height)
Add a Frac node before the UV Tile node
This is a simpler shader change, but has the downside that your geometry will no longer be cleanly supported in other shaders.
Thanks rutter!
i've implemented your solution into my shader and now it works perfectly!
so for everyone looking for this here is the shader im using now
Cheers, M

Atlas UV map vs. Local UV map

I want glow on my sprites using the UV coordinates, but the problem is, if the sprite originates from an atlas created by Unity's sprite packer, then the UV aren't normalized from 0 to 1, but from and to two arbitrary values. How do I normalize UV data for a single sprite that resides in an atlas? Am I required to parse additional information into the shader or should I already have the necessary information to do this process? The image below describes the situation:
The hand to the left is a sprite not from an atlas. The hand on the right is a sprite from an atlas. I want the right hand to look the same as the hand on the left.
I am not that familiar with shaders yet, so I am reliant to using shaderforge. I am using the following shaderforge layout:
You probably already know this, but the fundamental problem is the output of your "UV Coords" node. The other nodes in your shader are expecting normalized UVs ranging from 0 to 1, but that's not what you're getting when you use the texture atlas.
I can think of two ways to solve that. They're both viable, so I'd recommend trying whichever one fits more cleanly into your workflow.
Add a second UV channel
It's easy to treat UV0 as the only UV channel, but for certain techniques it can be helpful to add multiple UV coords to each vertex.
As an example, lightmapping is a popular feature where each model has its own individual textures (diffuse/normal/etc), but each scene has a pre-baked lightmap texture that is shared between multiple models -- sort of like an atlas for lighting information. UVs for these will not match, so the lightmap UVs are stored on a second channel (UV1).
In a similar fashion, you could use UV0 for atlas UVs and UV1 for local UVs. That gives you clean input on the [0,1] range that you can use for that multiply effect.
Add material params
You could scale and offset the UVs so that they are normalized.
Before rendering, find the min and max values for the mesh's UV coords
Pass those values in as material parameters
Add shader nodes to scale and offset the input UV, such that the range is normalized
For example, you could add min to each UV (offset), then divide by max - min (scale).