Hi,
Here’s the shader I could write:
Shader
Shader "Custom/RotationMap"
{
Properties{
_Unwrap("Unwrap", Range(0, 1)) = 1
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
#include "UnityCG.cginc"
static const float RAD2DEG = 57.295779513;
static const float DEG2RAD = 0.01745329252;
float _Unwrap;
struct v2g
{
float2 uv : TEXCOORD0;
float4 vertex : POSITION;
float3 worldPos : TEXCOORD1;
};
v2g vert(appdata_full v)
{
v2g o;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
struct g2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
fixed4 col : COLOR;
};
//CLOCKWISE: NEGATIVE
//COUNTER CLOCKWISE: POSITIVE
float signed_angle(float2 a, float2 b) {
a = normalize(a);
b = normalize(b);
return atan2( a.x*b.y - a.y*b.x, a.x*b.x + a.y*b.y );
}
float3 RotateAroundX (float3 v, float angle)
{
return float3(
v.x,
cos(angle)*v.y - sin(angle)*v.z,
sin(angle)*v.y + cos(angle)*v.z
);
}
float3 RotateAroundY (float3 v, float angle)
{
return float3(
cos(angle)*v.x + sin(angle)*v.z,
v.y,
-sin(angle)*v.x + cos(angle)*v.z
);
}
float3 RotateAroundZ (float3 v, float angle)
{
return float3(
cos(angle)*v.x - sin(angle)*v.y,
sin(angle)*v.x + cos(angle)*v.y,
v.z
);
}
[maxvertexcount(3)]
void geom(triangle v2g IN[3], inout TriangleStream<g2f> tristream)
{
g2f o;
//get vertices
float3 v0 = IN[0].worldPos;
float3 v1 = IN[1].worldPos;
float3 v2 = IN[2].worldPos;
float3 v00 = v0, v11 = v1, v22 = v2;
float colorv0, colorv1, colorv2;
//reorient triangle so that its normal becomes perpendicular to the xy
//compute normal
float3 n = normalize(cross(v1-v0, v2-v0));
//angle needed to align vector's y angle to the y axis
float yAngle = (atan2(n.z, n.x) - atan2(1, 0));
//Rotate vertices around Y axis
v00 = RotateAroundY(v00, yAngle);
v11 = RotateAroundY(v11, yAngle);
v22 = RotateAroundY(v22, yAngle);
//Recompute normal after first rotation
n = normalize(cross(v11-v00, v22-v00));
//angle needed to align vector's x angle to the x axis
float xAngle = (atan2(n.y, n.z) - atan2(0, 1));
//Rotate vertices around X axis
v00 = RotateAroundX(v00, xAngle);
v11 = RotateAroundX(v11, xAngle);
v22 = RotateAroundX(v22, xAngle);
float2 edge, uvEdge;
float anglev0, anglev1, anglev2, angleEdge1, angleEdge2;
///// Angle of vertex v0 /////
edge = normalize(v11 - v00); //get 3D direction of the edge v0v1
uvEdge = float2(IN[1].uv.x, IN[1].uv.y) - float2(IN[0].uv.x, IN[0].uv.y); //get UV edge direction
angleEdge1 = signed_angle(edge, uvEdge); //compute angle between 3D and UV edges
edge = (v22 - v00); //get 3D direction of the edge v0v2
uvEdge = float2(IN[2].uv.x, IN[2].uv.y) - float2(IN[0].uv.x, IN[0].uv.y); //get UV edge direction
angleEdge2 = signed_angle(edge, uvEdge); //compute angle between 3D and UV edges
anglev0 = (angleEdge1+angleEdge2)*0.5; //vertex angle can be found by averaging its touching edges' angles
colorv0 = ((anglev0/PI)*0.5)+0.5; //remap channel from range [-PI,PI] to range [0,1]
///// Angle of vertex v1 /////
edge = normalize(v22 - v11); //get 3D direction of the edge v1v2
uvEdge = float2(IN[2].uv.x, IN[2].uv.y) - float2(IN[1].uv.x, IN[1].uv.y); //get UV edge direction
angleEdge1 = signed_angle(edge, uvEdge); //compute angle between 3D and UV edges
edge = (v00 - v11); //get 3D direction of the edge v1v0
uvEdge = float2(IN[0].uv.x, IN[0].uv.y) - float2(IN[1].uv.x, IN[1].uv.y); //get UV edge direction
angleEdge2 = signed_angle(edge, uvEdge); //compute angle between 3D and UV edges
anglev1 = (angleEdge1+angleEdge2)*0.5; //vertex angle can be found by averaging its touching edges' angles
colorv1 = ((anglev1/PI)*0.5)+0.5; //remap channel from range [-PI,PI] to range [0,1]
///// Angle of vertex v2 /////
edge = normalize(v11 - v22); //get 3D direction of the edge v2v1
uvEdge = float2(IN[1].uv.x, IN[1].uv.y) - float2(IN[2].uv.x, IN[2].uv.y); //get UV edge direction
angleEdge1 = signed_angle(edge, uvEdge); //compute angle between 3D and UV edges
edge = (v00 - v22); //get 3D direction of the edge v2v0
uvEdge = float2(IN[0].uv.x, IN[0].uv.y) - float2(IN[2].uv.x, IN[2].uv.y); //get UV edge direction
angleEdge2 = signed_angle(edge, uvEdge); //compute angle between 3D and UV edges
anglev2 = (angleEdge1+angleEdge2)*0.5; //vertex angle can be found by averaging its touching edges' angles
colorv2 = ((anglev2/PI)*0.5)+0.5; //remap channel from range [-PI,PI] to range [0,1]
float3 worldPos, objectPos;
worldPos = lerp(IN[0].worldPos, float3(IN[0].uv, 0), _Unwrap);
objectPos = mul(unity_WorldToObject, float4(worldPos, 1));
o.pos = UnityObjectToClipPos(objectPos);
o.uv = IN[0].uv;
o.col = float4(colorv0,colorv0,colorv0,1);
tristream.Append(o);
worldPos = lerp(IN[1].worldPos, float3(IN[1].uv, 0), _Unwrap);
objectPos = mul(unity_WorldToObject, float4(worldPos, 1));
o.pos = UnityObjectToClipPos(objectPos);
o.uv = IN[1].uv;
o.col = float4(colorv1,colorv1,colorv1,1);
tristream.Append(o);
worldPos = lerp(IN[2].worldPos, float3(IN[2].uv, 0), _Unwrap);
objectPos = mul(unity_WorldToObject, float4(worldPos, 1));
o.pos = UnityObjectToClipPos(objectPos);
o.uv = IN[2].uv;
o.col = float4(colorv2,colorv2,colorv2,1);
tristream.Append(o);
}
fixed4 frag(g2f i) : SV_Target
{
return i.col;
}
ENDCG
}
}
}
I want to recall you that the problem I’m trying to solve is to find the Z rotation to apply to a normal vector stored in a Normal Map in order to make it face the right direction when the UV map’s triangles are rotated compared to their corresponding 3D triangles.
So, this shader takes all the triangles of a model, it rotates them to be parallel to the XY plane and it (badly) computes the angle needed to rotate the normal vector stored in each pixel in to follow the UV triangle rotation.
The output color [0,1] of each pixel represents the angle in the range [-180°,180°].
Initially, I thought that I could find the wanted angle for each triange in this way:
-
In the geometry shader, get 3 vertices at a time, so get a triangle
-
Rotate the triangle to be in the XY plane (like an UV map)
-
Choose one edge of the triangle
-
Compute angle between this edge’s direction and the corresponding UV map’s esge’s direction.
-
Create the color based on this angle
Apply the same color on all the 3 vertices
-
Output the colored triangle
But I realized that one edge is not enough to get a complete information about the rotation the normal vector needs to follow the triangle rotation.
So now I’m trying to compute this angle by vertex, assigning a posibily different color to the 3 vertices. The procedure is now something like this:
-
In the geometry shader, get 3 vertices at a time, so get a triangle
-
Rotate the triangle to be in the XY plane (like an UV map)
-
For each vertex V
-
Get the two edges E1 and E2 that touch V
-
A1 = angle between E1 and its corresponding edge in the UV map
-
A2 = angle between E2 and itscorresponding edge in the UV map
-
Compute A = (A1+A2) / 2
-
Create the color based on this angle
-
Apply this color to V
-
Output the interpolated and colored triangle
I can’t really test if the produced values are good, but I think they aren’t because my baking tool doesn’t produce good results when using these angles to rotate normals.
Any ideas on how to correctly compute these angles?
My brain is exploding 
Thanks,
Francesco