Here's how I draw some shape defined by vertices not shown here.
Vector3 position = (5,5,1);
Matrix world = Matrix.CreateTranslation(position);
BasicEffect basicEffect = new BasicEffect(graphicsDevice);
Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, -20), new Vector3(0, 0, 100), Vector3.Up);
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
graphics.Viewport.AspectRatio,
1.0f,
100);
// Set BasicEffect parameters.
basicEffect.World = world;
basicEffect.View = view;
basicEffect.Projection = projection;
//....draw some shape with basicEffect
I would like to paint the same shape only farther away so that its center stays in the same (x,y) pixel on screen but it is overall smaller as it's more distant.
I've tried scaling the position vector but had no success with it:
position .Z *= 2;
position .X *= 2;
position .Y *= 2;
What's the right way to do this?
Think about it geometrically: moving the object away from the camera means moving it along a line defined by two points: the camera's position and the object's position.
Now it's easy!
1) Find the vector object-to-camera, i.e.
Vector3 direction = objectPosition - cameraPosition;
2) Move the object alongside that vector by a certain amount, that is:
2.1) Normalize the direction
direction.Normalize();
2.2) Move the object by an amount x in that direction
objectPosition += direction * x;
And there you have it.
Related
I'm programming a 3D game in C# monogame, and I would like the particles to always face the camera. The code below works for that. I only send the particles texture, size and rotation to HLSL, which allows me to calculate the corners on the GPU.
This is the code that works without the particles rotating
output.PositionWS is the vertex world position
CameraPosWS is the camera world position
size.x is just the X size of billboard
size.y is just the Y size of the billboard
output.PositionWS = mul(float4(input.inPositionOS, 1), World).xyz;
float3 ParticleToCamera = output.PositionWS - CameraPosWS;
float3 ParticleUp = float3(0, 0, 1);
float3 ParticleRight = normalize(cross(ParticleToCamera, ParticleUp));
finalPosition.xyz += ((input.inTexCoords.x - 0.5f) * size.x) * ParticleRight;
finalPosition.xyz += ((0.5f - input.inTexCoords.y) * size.y) * ParticleUp;
But I would like the particle to now rotate while still always facing the camera. I was thinking of using a matrix to transform the calculated corners of the billboard to rotate the billboard, but I haven't been able to get it to work.
This is the code with billboard rotation along the X axis which doesn't work
rotation is a rotation in radians
float3 ParticleToCamera = output.PositionWS - CameraPosWS;
float3 ParticleUp = float3(0, 0, 1);
float3 ParticleRight = normalize(cross(ParticleToCamera, ParticleUp));
float3x3 RotationMatrix = float3x3(
ParticleRight,
ParticleUp,
ParticleToCamera
);
float3 rotatedVertex = mul(float3(input.inPositionOS.xy, 0), RotationMatrix);
// Only apply rotation on the X axis
rotatedVertex.x = rotatedVertex.x * cos(rotation);
finalPosition.xyz += ((input.inTexCoords.x - 0.5f) * size.x) * rotatedVertex.x;
finalPosition.xyz += ((0.5f - input.inTexCoords.y) * size.y) * rotatedVertex.y;
You are overthinking this problem. The point of the Vertex shader is to transform from and object/world coordinate system into screen based coordinate system.
The orientation is already screen aligned.
I will assume that scaling is accounted for in the size variable. Otherwise, scale it using ParticleToCamera.z.
Please note the following code is incomplete without the initial finalPosition definition: I have assumed it to be PositionWS.
output.PositionWS = mul(float4(input.inPositionOS, 1), World).xyz;
float3 ParticleToCamera = output.PositionWS - CameraPosWS;
float3 ParticleUp = float3(0, 0, 1);
float3 ParticleRight = float3(1, 0, 0);
finalPosition.xyz += ((input.inTexCoords.x - 0.5f) * size.x) * ParticleRight;
finalPosition.xyz += ((0.5f - input.inTexCoords.y) * size.y) * ParticleUp;
Suppose I have a point which is not in (0, 0, 0) and a perspective camera which is looking at (0, 0, 0).
To my understanding, if I move the perspective camera along the z axis, the point on the screen should move as well. The further the camera is, the closer the point should be towards (0, 0) in screen coordinates.
In my C# program, the camera movement does not affect screen coordinates (x, y) at all. It only changes the z coordinate just like an orthogonal camera. Here is the minimal example:
Vector3 v = new Vector3(3.0f);
// Move camera z to -10 from the center
Matrix4x4 viewMatrix = Matrix4x4.CreateLookAt(new Vector3(0.0f, 0.0f, -10.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));
Matrix4x4 projectionMatrix = Matrix4x4.CreatePerspectiveFieldOfView((float)Math.PI / 3.0f, 1.0f, 0.1f, 100.0f);
Vector3 v1 = Vector3.Transform(v, viewMatrix * projectionMatrix);
Console.WriteLine(v1); //<-5.1961527, 5.1961527, 12.912912>
// Move camera z to -1 from the center
viewMatrix = Matrix4x4.CreateLookAt(new Vector3(0.0f, 0.0f, -1.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));
Vector3 v2 = Vector3.Transform(v, viewMatrix * projectionMatrix);
Console.WriteLine(v2); //<-5.1961527, 5.1961527, 3.903904>
What is wrong in my reasoning?
Maybe the W of projected coord is not 1.
In vertex shaders, the returned W means point or vector. If 1, it is point. But for some reason, the vertex shaders automately divide XYZ by W. Therefore, the vertex shader need not to set W as 1 explicitly and almost of 3D math libraries return W as divisor instead of 1.
If you want to get proper projected coord, manually divide. Vector4.Transform() and divide XYZ by the W.
I am not well versed in matrix math so any help would be appreciated. So I have a simple ship sprite that rotates and moves. In order to calculate the bounding rectangle I tried using a rotation matrix and the code I got off of these tutorials:
http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel
But whenever the sprite rotates it's X and Y position of the bounding rectangle change drastically, by like 100s of pixels. The method for calculating the new bounding rectangle is as follows:
public static Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
{
// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);
// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
I am assuming this works since it comes from the Microsoft site and all. So I think the problem is with my matrices. I thought that If I only used a rotation matrix it would only spin the thing; not change its X and Y coordinates drastically. right now this is what I have:
// calculate transformation
Matrix transformation = Matrix.CreateRotationZ((float)rotation) * Matrix.CreateRotationZ((float)rotation);;
//update bounding rectangle
rectangle = new Rectangle((int)(position.X - origin.X), (int)(position.Y - origin.Y), texture.Width, texture.Height);
rectangle = BoundingAndCollision.CalculateBoundingRectangle(rectangle, transformation);
I tried a variety of other things including without the origin matrix and different orders of them. I also tried what a tutorial said was a general one that would work for anything, but that yielded the basically the same results:
Matrix transformation = Matrix.CreateTranslation(new Vector3(origin, 0.0f)) *
Matrix.CreateRotationZ((float)rotation) *
Matrix.CreateScale(scale) *
Matrix.CreateTranslation(position.X, position.Y, 0);
If that is not clear enough I can post screenshots, just let me know.Thank you in advance for the help!
A rotation matrix rotates around 0,0 and your rectangle is already placed in the world.
To solve first subtract the rectangle's center from each vertex (translate the rectangle to be centered at 0,0) and then add it again after rotating (place again on original location). In the code im assuming Y goes from top to bottom:
public static Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
{
Vector2 center = new Vector2(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2);
// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top) - center;
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top) - center;
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom) - center;
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom) - center;
// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
leftTop += center;
rightTop += center;
leftBottom += center;
rightBottom += center;
// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
I am trying to apply a transformation on a rectangle within another rectangle.... and having quite a bit of difficulty. Here is an example of what I am trying to achieve, the rotation will always be in increments of 90 degrees:
I have the bottom left X/Y, width, and height of both the outer and inner rectangles.... I'm trying to calculate these same values for the transformed inner rectangle.
My attempt can be found below, I tried rotating all 4 corners around the center of the large rect then put them back together as a rect. This may not work because the large rect width/height changes during the rotation. Does anyone know of a formula to accomplish this? If someone could point me to some good resource that would be fantastic.
My Code:
Vector2 center = new Vector2(largeRectWidth / 2.0f, largeRectHeight / 2.0f);
Rect innerRectRotated = RotateRectangleAroundPivot(innerRect, center, this.Rotation);
public static Rect RotateRectangleAroundPivot(Rect rect,
Vector2 pivot,
float rotation)
{
Vector2 leftTop = new Vector2(rect.x, rect.y + rect.height);
Vector2 rightTop = new Vector2(rect.x + rect.width, rect.y + rect.height);
Vector2 leftBottom = new Vector2(rect.x, rect.y);
Vector2 rightBottom = new Vector2(rect.x + rect.width, rect.y);
leftTop = RotatePointAroundPivot(leftTop, pivot, rotation);
rightTop = RotatePointAroundPivot(rightTop, pivot, rotation);
leftBottom = RotatePointAroundPivot(leftBottom, pivot, rotation);
rightBottom = RotatePointAroundPivot(rightBottom, pivot, rotation);
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
return new Rect(min.x, min.y, (max.x - min.x), (max.y - min.y));
}
public static Vector2 RotatePointAroundPivot(Vector2 point, Vector2 pivot, float angle)
{
angle = angle * Mathf.PI / 180.0f;
return new Vector2((float)(Math.Cos(angle) * (point.x - pivot.x) - Math.Sin(angle) * (point.y - pivot.y) + pivot.x), (float)(Math.Sin(angle) * (point.x - pivot.x) + Math.Cos(angle) * (point.y - pivot.y) + pivot.y));
}
well in 2D for 90deg rotations you do not need any trigonometry
use this instead:
input point (x,y) is rotated by 90deg like this:
xx=y; yy=-x;
the other direction is:
xx=-y; yy=x;
which formula is CW and CCW depends on your coordinate system
booth rotates point around point (0,0) by 90deg
so if you want to rotate by any other point just shift the points before and after ...
so do it like this:
translate point to your center of rotation
x-=x0; y-=y0;
rotate left or right
for example:
aa=x; bb=y; x=-bb; y=+aa;
translate point back
x+=x0; y+=y0;
now the x,y holds the rotated point
[notes]
if you want the center of rotation to be the middle of rectangle
then the x0,y0 is average point ...
so if rectangle is defined as x1,y1,...,x4,y4
then x0=(x1+x2+x3+x4)/4.0; and y0=(y1+y2+y3+y4)/4.0;
I'm having a quad that I constructed and I would like to scale the quad based on how much light, the problem is the dot product gives me negative values, which I can not use to scale the vectors on the other side of the quad. I have a mesh consists of 6 vertices, two quads. One of the two quads should extend or shrink based on how much is the dot product values, how would I scale one quad and shrink the other side based on that dot product value ?
float lightAngleRightVector = Vector3.Dot(lightDir.normalized, Source.transform.right.normalized);
lightAngleRightVector = Mathf.Clamp(lightAngleRightVector, 0.2f, 0.5f);
Global.Log("Light Angle Right Vecotr" + lightAngleRightVector);
// light projected left side, limit values);
if (lightAngleRightVector < 0.3f)
{
vxAbLeft = lightAngleRightVector;
vxCdRight = lightAngleRightVector - 0.1f;
}
// light projected right side
else if (lightAngleRightVector > 0.3f)
{
vxCdRight = lightAngleRightVector;
vxAbLeft = lightAngleRightVector - 0.1f;
}
Global.Log("VxCDRIGHT = " + vxCdRight);
Global.Log("vxAbLeft = " + vxAbLeft);
// add little bit shift up for fixing z-fighting
Vector3 vxPos1Top = (frontPt + new Vector3(0, mShadowOffestY, 0)) - (mRightFrontPt * vxAbLeft) * scale; // 1,2 vertices or on its left
Vector3 vxPos2Top = (mRightBackPt * vxAbLeft) * scale;
Vector3 vxPos3Top = frontPt;
Vector3 vxPos4Top = backPt;
Vector3 vxPos5Top =(mRightFrontPt * vxCdRight) * scale; // 5,6 vertices are on the right of the car
Vector3 vxPos6Top =(mRightBackPt * vxCdRight * scale);
Perhaps the scale should be abs( scale ), so it will be > 0 from the unlit side. Is that what you want?