- Home /
How to rotate a triangle created in a geometry shader around its center
I am splitting a quad made of two triangles into 2^n triangles using a tessellation shader where n is how many triangles are generated from each of the two triangles(Currently set to 3). In the geometry shader, I want to take the new triangles and rotate them in place. Even when I offset them based on the center, they still seem to be rotating around a point that isn't their center. The triangles closer to the origin, however, seem to be messed up a lot less.
This is just a few seconds into the rotation. As you can see, the triangles near the center are a lot less offset than the ones farther away. Here is the geometry shader code. The uv1 stores the vertex position in object space. t is a uniform that represents the timestep of the rotation.
float4 worldOne = mul(unity_ObjectToWorld, g0.data.uv1);
float4 worldTwo = mul(unity_ObjectToWorld, g1.data.uv1);
float4 worldThree = mul(unity_ObjectToWorld, g2.data.uv1);
float3 u = normalize(cross(worldTwo - worldOne, worldThree - worldOne));
float center = (worldOne + worldTwo + worldThree) / 3;
float3 pos1 = worldOne - center;
worldOne = float4(rotatePosition(u, t * .25, pos1) + center,1);
float3 pos2 = worldTwo - center;
worldTwo = float4(rotatePosition(u, t * .25, pos2) + center,1);
float3 pos3 = worldThree - center;
worldThree = float4(rotatePosition(u, t * .25, pos3) + center,1);
float3 objectOne = mul(unity_WorldToObject, worldOne);
float3 objectTwo = mul(unity_WorldToObject, worldTwo);
float3 objectThree = mul(unity_WorldToObject, worldThree);
g0.data.vertex = UnityObjectToClipPos(objectOne);
g1.data.vertex = UnityObjectToClipPos(objectTwo);
g2.data.vertex = UnityObjectToClipPos(objectThree);
and the helper function:
float3 rotatePosition(float3 axis, float angle, float3 p)
{
float3 n = axis; // the axis to rotate about
// Specify the rotation transformation matrix:
float3x3 m = float3x3(
n.x*n.x * (1.0f - cos(angle)) + cos(angle), // column 1 of row 1
n.x*n.y * (1.0f - cos(angle)) + n.z * sin(angle), // column 2 of row 1
n.x*n.z * (1.0f - cos(angle)) - n.y * sin(angle), // column 3 of row 1
n.y*n.x * (1.0f - cos(angle)) - n.z * sin(angle), // column 1 of row 2
n.y*n.y * (1.0f - cos(angle)) + cos(angle), // ...
n.y*n.z * (1.0f - cos(angle)) + n.x * sin(angle), // ...
n.z*n.x * (1.0f - cos(angle)) + n.y * sin(angle), // column 1 of row 3
n.z*n.y * (1.0f - cos(angle)) - n.x * sin(angle), // ...
n.z*n.z * (1.0f - cos(angle)) + cos(angle) // ...
);
// Apply the rotation to our 3D position:
float3 q = mul(m,p);
return q;
}
What am I doing wrong/missing?
All rotation is actually done based on where 0 is or in 3d (0, 0, 0). In object space, your quad probably has its 0 point in the center but these new triangles you are making do not. The trick is to first translate each point in your sub-triangles so that they are centered around 0 and then perform the rotation. Then, undo the translation you first performed and you will have a triangle that is rotated around its original location.
Thank you for your quick response! What would this translation consist of? Would I literally subtract the position from itself, rotate, and add the position back?
I think what you're looking for is for each triangle to rotate around its current center right? If so, then you'll need to compute that center: (v1 + v2 + v3) / 3. That's what you subtract because afterward your coordinates will be relate to this new center point (your new 0).
Answer by Bunny83 · Jan 13, 2018 at 01:40 AM
Your main issue seems to be that you have declared your center variable as float, not as float3. Since HLSL allows mixing of vector and scalar values it won't generate a compiler error but the result is not what you would expect.
Don't ask me what center would actually contain in this case:
float center = (worldOne + worldTwo + worldThree) / 3;
I would have thought that this line should generate an error sinceyour worldOne / Two / Three variables are float4. Dividing by 3 should also result in a float4. Possible values would probably by simply the "x" value.
When you subtract the "center" from your actual position you actually subtract the same value from all three components.
So just delcare the center variable as float3 or 4.
Note the way you rotate your points is very inefficient. The point of constructing a matrix is to reuse it. You should alter your method to just return a matrix. Since the axis and angle is the same for the 3 points you need to create the matrix only once.
float3x3 createRotation(float3 axis, float angle)
{
// ...
return m;
}
float3 center = (worldOne.xyz + worldTwo.xyz + worldThree.xyz) / 3;
float3x3 rotation = createRotation(u, t * .25);
worldOne = float4(mul(rotation, worldOne.xyz - center) + center,1);
worldTwo = float4(mul(rotation, worldTwo.xyz - center) + center,1);
worldThree = float4(mul(rotation, worldThree.xyz - center) + center,1);
Oh my god! I cannot believe I didn't catch that!. Thank you so much for your help!
Don't worry, it almost slipped my eye as well ^^. I spend a lot time to verify your rotation matrix is correct (which is but it seems to be inverted / transposed when you compare it with the one given on wikipedia. So you basically rotate the other way round). Finally by following the code step by step the "float" accidentally hit my eye ^^.
Ahh, i just found it in the HLSL documentation:
Vectors may be converted to scalar types (the first element of the vector is selected). A warning is issued if this is done implicitly.
So according to this statement you should actually get a warning. Though i'm not sure if Unity displays all warnings from the shader compiler.
Would the warnings be in the compiled shader code or in editor?
Well, if you select the shader asset and look at the inspector it usually shows errors direction in the inspector. However errors usually generate an error log in the console. Though as i said i'm not sure if Unity actually shows warnings.