Преобразовать Кватернион в Сферические углы

Статус: Offline
Реєстрація: 14.06.2007
Повідом.: 13581
Преобразовать Кватернион в Сферические углы

Короче нужно написать функции преобразования кватерниона в сферические углы вращения. И обратно :)

Почитал инет, написал, но работает не правильно, хз почему :confused: Может кто-то поможет разобраться? :пиво:

Вот код, как видно из теста, одно значение угла вычисляется правильно, но без знака, и оно может попадать не на ту ось вращения... Остальные вообще не соответствуют... :(:
Код:
using System;


public class Vec3
{
	public float x, y, z;
	
	public Vec3(float vx, float vy, float vz)
	{
		x = vx;
		y = vy;
		z = vz;
	}

	public override string ToString()
	{
		return string.Format("[x={0},y={1},z={2}]", x, y, z);
	}
}

public class Quat
{
	public Vec3 v;
	public float w;

	public Quat(Vec3 vv, float vw)
	{
		v = vv;
		w = vw;
	}

	public Quat(Spheric s)
	{
		float radians = (float)(180.0D / Math.PI);
		float sin_a = (float)Math.Sin( s.angle / (2.0f * radians));
		float cos_a = (float)Math.Cos( s.angle / (2.0f * radians));
		float sin_lat = (float)Math.Sin( s.latitude/radians );
		float cos_lat = (float)Math.Cos( s.latitude / radians);
		float sin_long = (float)Math.Sin( s.longitude / radians);
		float cos_long = (float)Math.Cos( s.longitude / radians);
		float X = sin_a * cos_lat * sin_long;
		float Y = sin_a * sin_lat;
		float Z = sin_a * sin_lat * cos_long;
		float W = cos_a;
		v = new Vec3(X, Y, Z);
		w = W;
	}

	public Spheric ToSpheric()
	{
		return new Spheric(this);
	}
	
	public override string ToString()
	{
		return string.Format("[v={0}, w={1}]", v, w);
	}
}

public class Spheric
{
	public float latitude;
	public float longitude;
	public float angle;

	public Spheric(Quat q)
	{
		float radians = (float)(180.0D / Math.PI);
		float cos_angle = q.w;
		float sin_angle = (float)(Math.Acos(cos_angle) * 2 * radians);
		angle = (float)(Math.Acos(cos_angle) * 2 * radians);
		if (Math.Abs(sin_angle) < 0.0005f)
			sin_angle = 1.0f; //?? sa
		float tx = q.v.x / sin_angle; //?? sa
		float ty = q.v.y / sin_angle; //?? sa
		float tz = q.v.z / sin_angle; //?? sa
		latitude = (float)-Math.Asin(ty);
		longitude = 0.0f;
		if (tx * tx + tz * tz >= 0.0005)
			longitude = (float)(Math.Atan2(tx, tz) * radians);
		if (longitude < 0)
			longitude += 360.0f;
	}

	public Quat ToQuat()
	{
		return new Quat(this);
	}

	public override string ToString()
	{
		return string.Format("[lat={0}, long={1}, ang={2}]", latitude, longitude, angle);
	}
}

public class Program
{
	static void Main()
	{
		showData(new Quat(new Vec3(-0.007F, 0.0F, -0.006F), 1.0F),  "0, 0, 0");
		showData(new Quat(new Vec3(0.0F, -0.007F, 1.0F), 0.0F),     "0, 0, 180");
		showData(new Quat(new Vec3(0.0F, 0.0F, -0.709F), 0.705F),   "0, 0, -90");
		showData(new Quat(new Vec3(0.0F, 0.0F, 0.707F), 0.706F),    "0, 0, 90");
		showData(new Quat(new Vec3(0.385F, 0.0F, 0.0F), 0.923F),    "45, 0, 0");
		showData(new Quat(new Vec3(-0.389F, 0.0F, 0.0F), 0.921F),   "-45, 0, 0");
		showData(new Quat(new Vec3(0.0F, -0.385F, 0.923F), 0.001F), "-45, 0, 180");

		//showData(new Quat(new Vec3(0.0001F, -0.007F, 0.9999F), 0.0001F));
		//showData(new Quat(new Vec3(0.385F, 0.0001F, 0.0001F), 0.923F));
	}

	static void showData(Quat q, string realData)
	{
		Console.WriteLine("TEST {0}", realData);
		Console.WriteLine("Quat={0}", q);
		Console.WriteLine("{0}", q.ToSpheric());
		Console.WriteLine("{0}", q.ToSpheric().ToQuat());
		Console.WriteLine();
	}
}

вывод:
TEST 0, 0, 0
Quat=[v=[x=-0.007,y=0,z=-0.006], w=1]
[lat=0, long=0, ang=0]
[v=[x=0,y=0,z=0], w=1]

TEST 0, 0, 180
Quat=[v=[x=0,y=-0.007,z=1], w=0]
[lat=3.888889E-05, long=0, ang=180]
[v=[x=0,y=6.787392E-07,z=6.787392E-07], w=1.83356E-08]

TEST 0, 0, -90
Quat=[v=[x=0,y=0,z=-0.709], w=0.705]
[lat=0, long=0, ang=90.34091]
[v=[x=0,y=0,z=0], w=0.705]

Значения после слова TEST - это реальные значения сферических углов.
Quat - это реальное значение кватерниона (взяты отладчиком из 3D игрушки).
Далее идет строка с вычисленными из кватерниона сферическими координатами.
И последняя строка - кватернион расчитанный на базе вычисленных сферических координат.
 
Код:
void Qaternion::Angles(double & angle1, double & angle2, double & angle3)
{
	double sqr_q0, sqr_q1, sqr_q2, sqr_q3, buf1, buf2, buf3, cos_angle2;

	sqr_q0 = q[0] * q[0];
	sqr_q1 = q[1] * q[1];
	sqr_q2 = q[2] * q[2];
	sqr_q3 = q[3] * q[3];

	buf1 = sqr_q0 + sqr_q1 - sqr_q2 - sqr_q3;
	buf2 = sqr_q0 + sqr_q2 - sqr_q3 - sqr_q1;

	angle2 = asin(2*(q[0]*q[3]+q[2]*q[1]));
	cos_angle2 = cos(angle2);

	double temp = 2*(q[0]*q[2]-q[3]*q[1])/cos_angle2;
	if (fabs(temp) > 1.0)
		temp = 1.0 * SIGN(temp);

	buf3 = asin(temp);

	buf1 >= 0 ? (angle1 = buf3) : (angle1 = PI-buf3);

	temp = 2*(q[0]*q[1]-q[2]*q[3])/cos_angle2;
	if (fabs(temp) > 1.0)
		temp = 1.0 * SIGN(temp);

	buf3 = asin(temp);

	buf2 >= 0 ? (angle3 = buf3) : (angle3 = PI-buf3);

	angle1 = mainValueAngle(angle1);
	angle2 = mainValueAngle(angle2);
	angle3 = mainValueAngle(angle3);
}
 
Код:
void Qaternion::Angles(double & angle1, double & angle2, double & angle3)
{
...
	angle1 = mainValueAngle(angle1);
	angle2 = mainValueAngle(angle2);
	angle3 = mainValueAngle(angle3);
}

спасибо огромное :) :пиво:

и можно еще на этот метод взглянуть?

double mainValueAngle(double angle)
 
Останнє редагування:
Незачто :)
вот:
Код:
#define	SIGN(x)		((x) < 0 ? -1 : 1)
double mainValueAngle(double x)
{	
	return acos(cos(x)) * SIGN(sin(x));
}
 
стал разбирать код и... непонятна структура q в вашем классе Quaternion, что она хранит?
Вначале подумал что это x,y,z,w в массиве, но вглядевшить в расчеты пришел к выводу что это чтото инное, похоже какая-то матрица? что такое q[0] например?


Код:
	sqr_q0 = q[0] * q[0];
	sqr_q1 = q[1] * q[1];
	sqr_q2 = q[2] * q[2];
	sqr_q3 = q[3] * q[3];
 
q[0] - скалярная составляющая кватерниона
q[1],q[2],q[3] - векторная составляющая кватерниона.
 
q[0] - скалярная составляющая кватерниона
q[1],q[2],q[3] - векторная составляющая кватерниона.

хм, меня смутило вот это q[0]*q[3]+q[2]*q[1], показалось что тут нету скаляра, сейчас попробую :)

фантастика! Все работает! Еще раз спасибо! :D :клас::апплодисм

а обратной функции у вас нет - из углов получить кватернион? :rolleyes:
 
Останнє редагування:
Получить из углов кватернион - это три последовательных поворота, которые описываются тремя кватернионами.
в результате нужно будет перемножить три кватерниона
L = L1*L2*L3

Кватернионы имеют следующий вид:
cos(angle/2) - скалярная часть
sin(angle/2)*e - векторная часть , здесь е - единичный вектор, вокруг которого осуществляем поворот.

Если будут вопросы, пишите, но отвечу уже завтра скорее всего :)
 
Получить из углов кватернион - это три последовательных поворота, которые описываются тремя кватернионами.
в результате нужно будет перемножить три кватерниона
L = L1*L2*L3

Кватернионы имеют следующий вид:
cos(angle/2) - скалярная часть
sin(angle/2)*e - векторная часть , здесь е - единичный вектор, вокруг которого осуществляем поворот.

Если будут вопросы, пишите, но отвечу уже завтра скорее всего :)

получилось, работает :пиво::yahoo:


Вообще мне изначально нужно было из кватерниона получить вектор направления, обработать его, получить новый вектор направления и затем произвести обратное преобразование.

Получить вектор направления из кватерниона насколько я понимаю можно умножив вектор начального направления на кватернион. Но вот как обратно из двух векторов получить кватернион не очень понятно.

Нашел
Тільки зареєстровані користувачі бачать весь контент у цьому розділі
для преобразования вектора направления в кватернион, переписал на си, но он почему-то работает неправильно:
Код:
Quat DirToQuat2(Vec3 vDir)
{
	// Step 1. Setup basis vectors describing the rotation given the input vector and assuming an initial up direction of (0, 1, 0)
	Vec3 vUp(0,1,0);           // Y Up vector
	Vec3 vRight = vUp.Cross(vDir);    // The perpendicular vector to Up and Direction
	vUp = vDir.Cross(vRight);            // The actual up vector given the direction and the right vector
            
	// Step 2. Put the three vectors into the matrix to bulid a basis rotation matrix
	// This step isnt necessary, but im adding it because often you would want to convert from matricies to quaternions instead of vectors to quaternions
	// If you want to skip this step, you can use the vector values directly in the quaternion setup below
	Matrix44 m(
		vRight.x, vRight.y, vRight.z, 0.0f,
		vUp.x, vUp.y, vUp.z, 0.0f,
		vDir.x, vDir.y, vDir.z, 0.0f,
		0.0f, 0.0f, 0.0f, 1.0f);
            
	// Step 3. Build a quaternion from the matrix
	float W = (float)sqrt(1.0f + m.m00 + m.m11 + m.m22) / 2.0f;
	float dfWScale = W * 4.0;
	float X = (float)((m.m21 - m.m12) / dfWScale);
	float Y = (float)((m.m02 - m.m20) / dfWScale);
	float Z = (float)((m.m10 - m.m01) / dfWScale);
	printf("$3DirToQuat2(): w=%f, v=(%f, %f, %f)", W, X, Y, Z);
	Quat q(W, X, Y, Z);
	q.Normalize();
	return q;
}

для вектора (0, -1, 0) получается кватерион (w=0.707107, v=(-0.707107, 0, 0)), а должен быть (w=0, v=(0, 0, 1))
 
Останнє редагування:
Напиши мне лучше в аську (в профиле), не совсем понятно, что тебе нужно.
 
Не мог пройти мимо ,не помогши товарищу.
При возникновении подобных проблем,выраженных в получении неправильного результата при теоретически верном формировании последовательности ,которая должна бы привести к желаемому результату(одна из сложно разрешаемых проблем,с которыми часто сталкивается кодяровщик),следует совершать ручной проход с анализом пошаговой итерации.
Такое разрешение дает применение следующей функции :


Код:
Вход в программу Клеза
dword_spasibo naiti_oshibka(char * str);

Строка передаваемых параметров имеет вид:
время,режим
время-пулулуние или полнолуние
режим -исправить или показать
 
Вобщем решение найдено :)

Вот более оптимальный и изящный способ получения сферических углов из кватерниона:
Код:
	template<class F1> explicit ILINE Ang3_tpl( const Quat_tpl<F1>& q )
	{
		assert(q.IsValid());
		y = (F)asin_tpl(max((F)-1.0,min((F)1.0,-(q.v.x*q.v.z-q.w*q.v.y)*2)));
		if (fabs_tpl(fabs_tpl(y)-(F)(g_PI*0.5))<(F)0.01)	
		{
			x = (F)0;
			z = (F)atan2_tpl(-2*(q.v.x*q.v.y-q.w*q.v.z),1-(q.v.x*q.v.x+q.v.z*q.v.z)*2);
		} else {
			x = (F)atan2_tpl((q.v.y*q.v.z+q.w*q.v.x)*2, 1-(q.v.x*q.v.x+q.v.y*q.v.y)*2);
			z = (F)atan2_tpl((q.v.x*q.v.y+q.w*q.v.z)*2, 1-(q.v.z*q.v.z+q.v.y*q.v.y)*2);
		}
	}

Получение вектора направления из кватерниона lookAt заключается в умножении кватерниона на вектор ориентации мира, в моем случае - это вектор up (0,1,0).

Обратная операция - получение lookAt кватерниона из вектора направления:
Код:
template<typename F> ILINE void Quat_tpl<F>::SetRotationVDir( const Vec3_tpl<F>& vdir )
{
	assert(vdir.IsUnit(0.01f));
	//set default initialisation for up-vector	
	w=F(0.70710676908493042);	v.x=F((vdir.z+vdir.z)*0.35355338454246521);	v.y=F(0.0); 	v.z=F(0.0); 
	f64 l = sqrt(vdir.x*vdir.x+vdir.y*vdir.y);
	if (l>0.00001)	
	{
		//calculate LookAt quaternion
		Vec3r hv	=	Vec3r(vdir.x/l,vdir.y/l+1.0,l+1.0);
		f64 r = sqrt(hv.x*hv.x + hv.y*hv.y);
		f64 s	= sqrt(hv.z*hv.z + vdir.z*vdir.z);
		//generate the half-angle sine&cosine
		f64 hacos0=0.0;			f64 hasin0=-1.0;			
		if (r>0.00001) { hacos0=hv.y/r; hasin0=-hv.x/r; }	//yaw
		f64 hacos1=hv.z/s;	f64 hasin1=vdir.z/s;					//pitch
		w=F(hacos0*hacos1); v.x=F(hacos0*hasin1);	v.y=F(hasin0*hasin1);	v.z=F(hasin0*hacos1);  
	}
}
 
Для полноты картины, вот еще обратное преобразование - получение кватерниона из углов :клас::
Код:
template<typename F> ILINE void Quat<F>::SetRotationXYZ(const Ang3 &a)	
{ 
	assert(a.IsValid());
	F sx,cx;  sincosf(F(a.x*0.5),&sx,&cx);
	F sy,cy;  sincosf(F(a.y*0.5),&sy,&cy);
	F sz,cz;  sincosf(F(a.z*0.5),&sz,&cz);
	w   = cx*cy*cz + sx*sy*sz;
	v.x = cz*cy*sx - sz*sy*cx;
	v.y = cz*sy*cx + sz*cy*sx;
	v.z = sz*cy*cx - cz*sy*sx;
}

ILINE void sincosf (f32 angle, f32* pSin, f32* pCos) {	*pSin = f32(sin(angle));	*pCos = f32(cos(angle));	}
 
Хотелось бы увидеть пару десятков макрокоманд супервизора и используемый метод обработки данных,чтобы иметь более реальную картинку выполнения данного куска кода.
 
Хотелось бы увидеть пару десятков макрокоманд супервизора и используемый метод обработки данных,чтобы иметь более реальную картинку выполнения данного куска кода.

а по теме есть что сказать?
 
а по теме есть что сказать?

Что конкретно не понятно в отображаемых вами процессах ,готов пояснить.
Пока что не могу дать оценку вашим исследованиям,чтобы предложить Вам положение в пром -акции.
Укажите практическое применение теории,а также проведите полное тестирование вычислительного алгоритма.



Однако же,хочу заметить как Лексусу,так и Клезу,занимающимися игровыми разработками,
они используют старые технолгии,это вчерашний день,в новом дне математики нет.
Важен вопрос,что было вначале,2пр или окружность ?
Было колесо ,описывалось оно совсем по иному,тогда число имело более расширенные свойства,потом прошли многие лета,раскопали колесо,и думали как описать его и наделили число свойствами сравнительной характеристики и единицы от "локтя" и заметили,что свойства колеса зависят от центра,нашли радиус(придумали ) потом волшебное число нашли и так начали описывать всевозможные свойства обьектов,меряя их и ограничивая в два прописанных сравнение и измерение,не догадываясь по сей день,что число может иметь и другое расширенное свойство и рассматриваться как сложная структура конкретно описывающей обьект в пространстве и обладающее более информативным описанием свойств обьекта.



а по теме есть что сказать?

Почему Фламинго,чтобы покушать ,должна пролететь сотни километров,а ты открыть холодильник и согнуть в локте руку ?
 
Останнє редагування:
Назад
Зверху Знизу