Sign in to follow this  
Spa8nky

Treating 6 quadrangles as a cube with a world transform.

Recommended Posts

Spa8nky    230
In order to batch draw my quadrangles the world transform is created in my HLSL shader. This means that all my quadrangles are defined in terms of scale, rotation and translation as separate values and not as one matrix.

If I wanted to create a cube then I would define a quadrangle for each cube face:

[code]

faces[0] = new Quadrangle(
position + (0.5f * size * -Vector3.UnitX),
size,
size,
-Vector3.UnitX,
0,
0
);
[/code]

where the Quadrangle constructor is as follows:

[code]

public Quadrangle(Vector3 position, float width, float height, Vector3 normal, int texture0Index, int texture1Index)
: this(position, width, height, RotationFrom(normal), texture0Index, texture1Index) { }
[/code]

The problem I would like some help solving is how can I treat all 6 faces as a cube when it comes to scaling, rotating and translating?

For example if the cube moves then each quadrangle face will have to move according to the centroid position. I'm currently doing this by storing the original position and adding the current position of the cube:

[code]

faces[0].Position = originalFacePositions[0] + cube.Position;
faces[1].Position = originalFacePositions[1] + cube.Position;
faces[2].Position = originalFacePositions[2] + cube.Position;
faces[3].Position = originalFacePositions[3] + cube.Position;
faces[4].Position = originalFacePositions[4] + cube.Position;
faces[5].Position = originalFacePositions[5] + cube.Position;
[/code]

This doesn't seem like the correct way to approach this as adopting the same technique for rotations doesn't produce the correct result:

[code]

faces[0].Rotation = localRotations[0] * rotation; // localRotation = original rotation for this face
faces[1].Rotation = localRotations[1] * rotation;
faces[2].Rotation = localRotations[2] * rotation;
faces[3].Rotation = localRotations[3] * rotation;
faces[4].Rotation = localRotations[4] * rotation;
faces[5].Rotation = localRotations[5] * rotation;
[/code]

How can I correctly scale, rotate and translate each quadrangle face so it would behave like a cube model?

Share this post


Link to post
Share on other sites
Numsgil    501
You shouldn't need to decompose the affine transformation before passing it to the shader. Just store the rows or columns in the per-vertex attributes like in POSITION1, TEXCOORD1, etc. and then reconstruct it first thing in the vertex shader.

There are some limitations on the sort of ranges and accuracies these attributes allow in older shader models, but I think everything from 3.0 and later has uniform, reasonable ranges and accuracies, so not something you have to worry about probably.

...

I don't understand the latter part of your post. Are you trying to apply a transformation to the original cube? Why? Why not just leave it centered at the origin? In fact, if you're going this route, don't be afraid to go full tilt. You can probably stuff all quads into a single draw call. Just make the vertex buffer large enough. Any overhead from larger vertex and index buffers, for instance, is more than offset by the lower draw call count. Then, there's no reason to set up the quads in the shape of a cube in the first place. You can set it up as n unit quads, centered at the origin, all overlapping. Each frame you update the attributes in the vertex buffer corresponding to the rows of your transformation matrix.

I do something similar in my own shader for drawing 2D shapes. I can show it if you're interested (still a work in progress, but might give you an idea).

Share this post


Link to post
Share on other sites
Spa8nky    230
[color="#1C2837"][size="2"]
[color="#1C2837"][size="2"][quote][/size][/color]You shouldn't need to decompose the affine transformation before passing it to the shader. Just store the rows or columns in the per-vertex attributes like in POSITION1, TEXCOORD1, etc. and then reconstruct it first thing in the vertex shader. [/size][/color]
[color="#1C2837"][size="2"][/quote][/size][/color]
[color="#1C2837"] [/color]
[size="2"][color="#1C2837"]Are you saying I should calculate the world transform on the CPU and pass that into the shader?[/color][/size]
[size="2"] [/size]
[size="2"][color="#1C2837"]I currently require the orientation as Up, Right and Normal for correct UV orientation (and lighting):[/color][/size]
[color="#1C2837"] [/color][size="2"][color="#1C2837"][code][/color][/size]
[size="2"][color="#1C2837"]
// [uv coordinates]
// • Keep textures vertically aligned to world y Axis (0,1,0) [Quad.Rotation.Up = Vector3.Cross(Vector3.UnitY, normal)]
// • Textures are aligned to vector 'v' where [Rotation.Up = Vector3.Cross(v, normal)]
float3 v0 = input.Position.xyz - float3(input.IData0.x, input.IData0.y, input.IData0.z);
float widthInverse = 1.0f / input.IData3.x; // 1.0f / Width
float heightInverse = 1.0f / input.IData3.y; // 1.0f / Height

// [Perspective 3D]
// • dot(v0, up)
input.TextureCoordinates.x = dot(v0, normalize(float3(input.IData1.z, input.IData1.w, input.IData2.x))) * heightInverse + 0.5f;

// • dot(v0, right)
input.TextureCoordinates.y = dot(v0, normalize(float3(input.IData0.w, input.IData1.x, input.IData1.y))) * widthInverse + 0.5f;[/color][/size]
[size="2"][color="#1C2837"][/code]
[/color][/size][color="#1C2837"] [/color]
[color="#1C2837"][size="2"]This would mean that I would have to pass in the world transform along with the rotation, or alternatively a non scaled world transform and scale into the shader. What would you recommend?[/size][/color]
[size="2"] [/size]
[size="2"][color="#1C2837"][quote][/color][/size]
[size="2"][color="#1C2837"]I don't understand the latter part of your post. Are you trying to apply a transformation to the original cube?[/color][/size]
[size="2"][color="#1C2837"][/quote][/color][/size]
[size="2"] [/size]
[size="2"][color="#1C2837"]- I have 6 quads (all batch drawn/single draw call) that create a cube of unit length.[/color][/size]
[size="2"][color="#1C2837"]- I have a physics engine that provides the world transform of a cube in the physics space[/color][/size]
[size="2"][color="#1C2837"]- I would like to represent the scale, rotation, transformation of the physics cube using the 6 quads[/color][/size]
[size="2"] [/size]
[size="2"][color="#1C2837"]However, I can't seem to transform the 6 quadrangles by the physics based cube's transform correctly.[/color][/size]
[size="2"] [/size]
[size="2"][color="#1C2837"][quote][/color][/size]
[size="2"][color="#1C2837"]You can set it up as n unit quads, centered at the origin, all overlapping. Each frame you update the attributes in the vertex buffer corresponding to the rows of your transformation matrix.[/color][/size]
[size="2"][color="#1C2837"][/quote][/color][/size]
[size="2"] [/size]
[size="2"][color="#1C2837"]If I am only provided with one world transform for the cube would it be better this way or would it be better to have the 6 quads arranged as a unit cube around the origin and transform them that way?[/color][/size]
[size="2"] [/size]
[size="2"][color="#1C2837"]Thanks for offering to help with this.[/color][/size]

Share this post


Link to post
Share on other sites
Spa8nky    230
I've rewritten my vertex format as suggested:

[code]

struct VertexShaderInputAlt
{
float4 Position : POSITION0; // Local Vertex Position
float4 TexCoordDimension : TEXCOORD0; // [x, y] Texture Coordinates [z, w] Width & Height
float4 Column1 : TEXCOORD1; // M11...M14
float4 Column2 : TEXCOORD2; // M21...M24
float4 Column3 : TEXCOORD3; // M31...M34
float4 Column4 : TEXCOORD4; // M41...M44
float4 SourceRectangle0 : TEXCOORD5; // Source Rectangle (Texture Layer 0)
float4 SourceRectangle1 : TEXCOORD6; // Source Rectangle (Texture Layer 1)
};
[/code]

If I create a cube out of quadrangles:

[code]

public static Quadrangle[] CreateCube()
{
Quadrangle[] faces = new Quadrangle[6];

// -X
faces[0] = new Quadrangle(
0.5f * -Vector3.UnitX,
1,
1,
-Vector3.UnitX,
0,
0
);

// +X
faces[1] =new Quadrangle(
0.5f * Vector3.UnitX,
1,
1,
Vector3.UnitX,
0,
0
);

// -Y
faces[2] = new Quadrangle(
0.5f * -Vector3.UnitY,
1,
1,
-Vector3.UnitY,
0,
0
);

// +Y
faces[3] = new Quadrangle(
0.5f * Vector3.UnitY,
1,
1,
Vector3.UnitY,
0,
0
);

// -Z
faces[4] = new Quadrangle(
0.5f * -Vector3.UnitZ,
1,
1,
-Vector3.UnitZ,
0,
0
);

// +Z
faces[5] = new Quadrangle(
0.5f * Vector3.UnitZ,
1,
1,
Vector3.UnitZ,
0,
0
);

return faces;
}
[/code]

and then transform the quadrangle vertices using new vertex format:

[code]

public void Transform(Matrix transform)
{
// Ignore scale as rotation and scale are required separately in the shader code
Matrix worldTransform = rotation;
worldTransform.M41 = position.X;
worldTransform.M42 = position.Y;
worldTransform.M43 = position.Z;
worldTransform.M44 = 1f;

worldTransform *= transform;

vertices[0].Column1.X = worldTransform.M11;
vertices[0].Column1.Y = worldTransform.M12;
vertices[0].Column1.Z = worldTransform.M13;
vertices[0].Column1.W = worldTransform.M14;
vertices[0].Column2.X = worldTransform.M21;
vertices[0].Column2.Y = worldTransform.M22;
vertices[0].Column2.Z = worldTransform.M23;
vertices[0].Column2.W = worldTransform.M24;
vertices[0].Column3.X = worldTransform.M31;
vertices[0].Column3.Y = worldTransform.M32;
vertices[0].Column3.Z = worldTransform.M33;
vertices[0].Column3.W = worldTransform.M34;
vertices[0].Column4.X = worldTransform.M41;
vertices[0].Column4.Y = worldTransform.M42;
vertices[0].Column4.Z = worldTransform.M43;
vertices[0].Column4.W = worldTransform.M44;

vertices[1].Column1.X = worldTransform.M11;
vertices[1].Column1.Y = worldTransform.M12;
vertices[1].Column1.Z = worldTransform.M13;
vertices[1].Column1.W = worldTransform.M14;
vertices[1].Column2.X = worldTransform.M21;
vertices[1].Column2.Y = worldTransform.M22;
vertices[1].Column2.Z = worldTransform.M23;
vertices[1].Column2.W = worldTransform.M24;
vertices[1].Column3.X = worldTransform.M31;
vertices[1].Column3.Y = worldTransform.M32;
vertices[1].Column3.Z = worldTransform.M33;
vertices[1].Column3.W = worldTransform.M34;
vertices[1].Column4.X = worldTransform.M41;
vertices[1].Column4.Y = worldTransform.M42;
vertices[1].Column4.Z = worldTransform.M43;
vertices[1].Column4.W = worldTransform.M44;

vertices[2].Column1.X = worldTransform.M11;
vertices[2].Column1.Y = worldTransform.M12;
vertices[2].Column1.Z = worldTransform.M13;
vertices[2].Column1.W = worldTransform.M14;
vertices[2].Column2.X = worldTransform.M21;
vertices[2].Column2.Y = worldTransform.M22;
vertices[2].Column2.Z = worldTransform.M23;
vertices[2].Column2.W = worldTransform.M24;
vertices[2].Column3.X = worldTransform.M31;
vertices[2].Column3.Y = worldTransform.M32;
vertices[2].Column3.Z = worldTransform.M33;
vertices[2].Column3.W = worldTransform.M34;
vertices[2].Column4.X = worldTransform.M41;
vertices[2].Column4.Y = worldTransform.M42;
vertices[2].Column4.Z = worldTransform.M43;
vertices[2].Column4.W = worldTransform.M44;

vertices[3].Column1.X = worldTransform.M11;
vertices[3].Column1.Y = worldTransform.M12;
vertices[3].Column1.Z = worldTransform.M13;
vertices[3].Column1.W = worldTransform.M14;
vertices[3].Column2.X = worldTransform.M21;
vertices[3].Column2.Y = worldTransform.M22;
vertices[3].Column2.Z = worldTransform.M23;
vertices[3].Column2.W = worldTransform.M24;
vertices[3].Column3.X = worldTransform.M31;
vertices[3].Column3.Y = worldTransform.M32;
vertices[3].Column3.Z = worldTransform.M33;
vertices[3].Column3.W = worldTransform.M34;
vertices[3].Column4.X = worldTransform.M41;
vertices[3].Column4.Y = worldTransform.M42;
vertices[3].Column4.Z = worldTransform.M43;
vertices[3].Column4.W = worldTransform.M44;

vertices[0].Normal = Normal;
vertices[1].Normal = Normal;
vertices[2].Normal = Normal;
vertices[3].Normal = Normal;

recreateWorld = false;
}
[/code]
then create the world position in the shader (including scale):

[code]

// [Rotation, Scale]
World._11 = input.Column1.x * input.TexCoordDimension.z; // * Scale X
World._12 = input.Column1.y * input.TexCoordDimension.z;
World._13 = input.Column1.z * input.TexCoordDimension.z;
World._14 = input.Column1.w;
World._21 = input.Column2.x * input.TexCoordDimension.w; // * Scale Y
World._22 = input.Column2.y * input.TexCoordDimension.w;
World._23 = input.Column2.z * input.TexCoordDimension.w;
World._24 = input.Column2.w;
World._31 = input.Column3.x;
World._32 = input.Column3.y;
World._33 = input.Column3.z;
World._34 = input.Column3.w;

// [Translation]
World._41 = input.Column4.x;
World._42 = input.Column4.y;
World._43 = input.Column4.z;
World._44 = input.Column4.w;

// [Transformation]
// • Multiplying input.Position by World, then the result by ViewProjection is fast
// • Concatenating World and ViewProjection matrices then multiplying input.Position by the result is slower
input.Position = mul(input.Position, World);
Output.Position = mul(input.Position, ViewProjection);
[/code]

the quadrangle positions do not scale correctly. This error can be shown with a model representation surrounded by the quadrangles:

[img]http://www.rhysperkins.com/XNA/QuadrangleCubeError.png[/img]


Each of the 6 faces should be where the tan cube model faces are but the scaling doesn't work. How can this be fixed?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this