CannibalSmith ([info]cannibalsmith) rakstīja [info]koderi kopienā,
@ 2008-10-29 13:31:00

Previous Entry  Add to memories!  Tell a Friend!  Next Entry
3D transformāciju matricas 2
Turpinot tēmu, uzrakstīju programmu, kas implementē vienkāršu scenegraph un zīmē 3D punktus. Bet rotācija ap Y un Z asīm strādā nepareizi.

Bubu, lūdzu, lūdzu, izpalīdzi! Kur man ir kļūda?

public class Vector
{
	public double x, y, z;
	
	public Vector(double x, double y, double z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public static Vector operator *(Vector u, Matrix v)
	{
		Vector w = new Vector();
		w.x = u.x * v.a + u.y * v.d * u.z * v.g + 1 * v.l;
		w.y = u.x * v.b + u.y * v.e + u.z * v.i + 1 * v.m;
		w.z = u.x * v.c + u.y * v.f + u.z * v.j + 1 * v.n;
		double h = u.x * v.p + u.y * v.q + u.z * v.r + 1 * v.s;
		w.x /= h;
		w.y /= h;
		w.z /= h;
		return w;
	}
}

public class Matrix
{
	public double a, b, c, p, d, e, f, q, g, i, j, r, l, m, n, s;

	public Matrix()
	{
		a = 1;
		e = 1;
		j = 1;
		s = 1;
	}

	public static Matrix operator *(Matrix u, Matrix v)
	{
		Matrix w = new Matrix();
		w.a = u.a * v.a + u.b * v.d + u.c * v.g + u.p * v.l;
		w.b = u.a * v.b + u.b * v.e + u.c * v.i + u.p * v.m;
		w.c = u.a * v.c + u.b * v.f + u.c * v.j + u.p * v.n;
		w.p = u.a * v.p + u.b * v.q + u.c * v.r + u.p * v.s;
		w.d = u.d * v.a + u.e * v.d + u.f * v.g + u.q * v.l;
		w.e = u.d * v.b + u.e * v.e + u.f * v.i + u.q * v.m;
		w.f = u.d * v.c + u.e * v.f + u.f * v.j + u.q * v.n;
		w.q = u.d * v.p + u.e * v.q + u.f * v.r + u.q * v.s;
		w.g = u.g * v.a + u.i * v.d + u.j * v.g + u.r * v.l;
		w.i = u.g * v.b + u.i * v.e + u.j * v.i + u.r * v.m;
		w.j = u.g * v.c + u.i * v.f + u.j * v.j + u.r * v.n;
		w.r = u.g * v.p + u.i * v.q + u.j * v.r + u.r * v.s;
		w.l = u.l * v.a + u.m * v.d + u.n * v.g + u.s * v.l;
		w.m = u.l * v.b + u.m * v.e + u.n * v.i + u.s * v.m;
		w.n = u.l * v.c + u.m * v.f + u.n * v.j + u.s * v.n;
		w.s = u.l * v.p + u.m * v.q + u.n * v.r + u.s * v.s;
		return w;
	}

	public static Matrix RotateX(double angle)
	{
		Matrix w = new Matrix();
		w.e = Math.Cos(angle);
		w.f = Math.Sin(angle);
		w.i = -Math.Sin(angle);
		w.j = Math.Cos(angle);
		return w;
	}

	public static Matrix RotateY(double angle)
	{
		Matrix w = new Matrix();
		w.a = Math.Cos(angle);
		w.c = -Math.Sin(angle);
		w.g = Math.Sin(angle);
		w.j = Math.Cos(angle);
		return w;
	}

	public static Matrix RotateZ(double angle)
	{
		Matrix w = new Matrix();
		w.a = Math.Cos(angle);
		w.b = Math.Sin(angle);
		w.d = -Math.Sin(angle);
		w.e = Math.Cos(angle);
		return w;
	}

	public static Matrix Translate(double x, double y, double z)
	{
		Matrix w = new Matrix();
		w.l = x;
		w.m = y;
		w.n = z;
		return w;
	}

	public static Matrix Scale(double x, double y, double z)
	{
		Matrix w = new Matrix();
		w.a = x;
		w.e = y;
		w.j = z;
		return w;
	}
}

public class Node
{
	Matrix transform;
	Node parent;
	List children;

	public Node()
	{
		transform = new Matrix();
		children = new List();
	}

	public Node(Node parent) : this()
	{
		Parent = parent;
	}

	public virtual void Render(Graphics graphics, Matrix transform)
	{
		foreach (Node i in children)
		{
			i.Render(graphics, i.transform * transform);
		}
	}

	public List Children
	{
		get { return children; }
	}

	public Node Parent
	{
		get { return parent; }
		set
		{
			if (parent != null)
			{
				parent.children.Remove(this);
			}
			if (value != null)
			{
				value.children.Add(this);
			}
			parent = value;
		}
	}

	public Matrix Transform
	{
		get { return transform; }
		set { transform = value; }
	}
}

public class Mesh : Node
{
	List vertices;
	Brush brush;

	public Mesh() : this(null)
	{
	}

	public Mesh(Node parent) : base(parent)
	{
		vertices = new List();
		brush = Brushes.White;
	}

	public override void Render(Graphics graphics, Matrix transform)
	{
		base.Render(graphics, transform);
		foreach (Vector i in vertices)
		{
			Vector j = i * transform;
			if (j.z > 0.1)
			{
				float size = (float)(0.05 / j.z);
				graphics.FillRectangle(brush, (float)(j.x / j.z) - size / 2, (float)(j.y / j.z) - size / 2, size, size);
			}
		}
	}

	public Brush Brush
	{
		get { return brush; }
		set { brush = value; }
	}

	public List Vertices
	{
		get { return vertices; }
	}	
}

public class Camera : Node
{
	public Camera()
	{
	}
	
	public Camera(Node parent) : base(parent)
	{
	}

	public void Render(Graphics graphics)
	{
		float w = graphics.VisibleClipBounds.Width / 2;
		float h = graphics.VisibleClipBounds.Height / 2;
		graphics.Transform = new System.Drawing.Drawing2D.Matrix(h, 0, 0, -h, w, h);
		Matrix transform = new Matrix();
		Node i;
		for (i = this; i.Parent != null; i = i.Parent)
		{
			transform = i.Transform * transform;
		}
		i.Render(graphics, transform);
	}
}

public partial class Window : Form
{
	Node root;
	Camera camera;
	Mesh cube;
	Mesh cube2;
	Mesh cube3;
	Mesh ground;

	public Window()
	{
		InitializeComponent();
		SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

		root = new Node();
		camera = new Camera(root);
		camera.Transform = Matrix.Translate(0, -2, 0);
		ground = new Mesh(root);
		ground.Brush = Brushes.Green;
		for (double i = -10; i <= 10; i += 1)
		{
			for (double j = -10; j <= 10; j += 0.05)
			{
				ground.Vertices.Add(new Vector(i, 0, j));
				ground.Vertices.Add(new Vector(j, 0, i));
			}
		}
		cube = new Mesh(root);
		cube.Transform = Matrix.Scale(0.5, 0.5, 0.5) * Matrix.Translate(2, 0.5, 3.5);
		for (double i = -1; i <= 1; i += 0.1)
		{
			cube.Vertices.Add(new Vector(i, -1, -1));
			cube.Vertices.Add(new Vector(-1, i, -1));
			cube.Vertices.Add(new Vector(-1, -1, i));
			cube.Vertices.Add(new Vector(i, 1, -1));
			cube.Vertices.Add(new Vector(1, i, -1));
			cube.Vertices.Add(new Vector(-1, 1, i));
			cube.Vertices.Add(new Vector(1, 1, i));
			cube.Vertices.Add(new Vector(1, -1, i));
			cube.Vertices.Add(new Vector(-1, i, 1));
			cube.Vertices.Add(new Vector(i, -1, 1));
			cube.Vertices.Add(new Vector(i, 1, 1));
			cube.Vertices.Add(new Vector(1, i, 1));
		}
		cube2 = new Mesh(root);
		cube2.Vertices = cube.Vertices;
		cube2.Transform = Matrix.Scale(0.5, 0.5, 0.5) * Matrix.Translate(0, 0.5, 3.5);
		cube3 = new Mesh(root);
		cube3.Vertices = cube.Vertices;
		cube3.Transform = Matrix.Scale(0.5, 0.5, 0.5) * Matrix.Translate(-2, 0.5, 3.5);
	}

	void Window_Paint(object sender, PaintEventArgs e)
	{
		camera.Render(e.Graphics);
	}

	void timer1_Tick(object sender, EventArgs e)
	{
		cube.Transform = Matrix.RotateZ(0.05) * cube.Transform;
		cube2.Transform = Matrix.RotateY(0.05) * cube2.Transform;
		cube3.Transform = Matrix.RotateX(0.05) * cube3.Transform;
		Invalidate();
	}
}


(Ierakstīt jaunu komentāru)


[info]bubu
2008-10-29 15:27 (saite)
Es galīgi nesaprotu kā tu tur projicē no world-space uz screen-space tos punktus. Tu tikai tur izdali ar z koordināti (Mesh.Render'ā) un viss. Bet tā jau nav projekcija.
Iesaku taisīt klasisku transformācijas ķēdīti ar matricām: view_space_matrix * world_space_matrix * projection_matrix.Z Kur projekcijas matrica aprakstīta, piemēram šeit - http://msdn.microsoft.com/en-us/library/bb147302.aspx Un tikai pēc tam veikt to dalīšanu ar Z. Tā būs daudz saprotamāk.

(Atbildēt uz šo) (Diskusija)


[info]bubu
2008-10-29 15:39 (saite)
Iespējams gan, ka tā nav īstā problēma droši vien. Tev tur ir vēl cita, aritmētiska problēma, vektora reizināšanā ar matricu (uz kuru norādīja subjekts K``):
w.x = u.x * v.a + u.y * v.d * u.z * v.g + 1 * v.l;
Te ir par daudz reizināšanas zīmju.

Iesaku nelietot tos abcdef... burtus. Tajos apmaldīties var. Vēl jo vairāk, ka tie nav alfabētiski pēc kārtas. Tā vietā lieto masīvu - vektoram 3 (vai 4), matricai 16 elementu. Un tad tās reizināšanas rakti nevis garā palagā ar visiem alfabēta burtiem, bet vienkāršos ciklos ar 3 (vai 4) iterācijām.

(Atbildēt uz šo) (Iepriekšējais) (Diskusija)


[info]cannibalsmith
2008-10-29 17:51 (saite)
Urā! Un es uz to kodu blenzu vairākas dienas.

Esmu tev parādā.

(Atbildēt uz šo) (Iepriekšējais)


Neesi iežurnalējies. Iežurnalēties?