Перейти к содержимому
  • 0
TC1061

Вращение 3D вектора

Вопрос

Ну вообщем, я пытался сделать воксельный рэйкастер. Мне нужна была функция вращения 3D вектора.
Она должна принимать 5 аргументов, координаты вектора (X,Y,Z) и углы вращения (по Y, по X) и вращать вектор вокруг нулевой точки (0,0,0). Я вот попытался свою функцию реализовать, но она работает неправильно:

 

 

local function rotateV3X(x,y,z,a)
	local a=math.rad(a)
	local m11,m12,m13=1,0,0
	local m21,m22,m23=0,math.cos(a),-math.sin(a)
	local m31,m32,m33=0,math.sin(a),math.cos(a)
	return x*m11+y*m12+z*m13,  x*m21+y*m22+z*m23,  x*m31+y*m32+z*m33
end
local function rotateV3Y(x,y,z,a)
	local a=math.rad(a)
	local m11,m12,m13=math.cos(a),0,math.sin(a)
	local m21,m22,m23=0,1,0
	local m31,m32,m33=-math.sin(a),0,math.cos(a)
	return x*m11+y*m12+z*m13,  x*m21+y*m22+z*m23,  x*m31+y*m32+z*m33
end
local function rotateV3Z(x,y,z,a)
	local a=math.rad(a)
	local m11,m12,m13=math.cos(a),-math.sin(a),0
	local m21,m22,m23=math.sin(a),math.cos(a),0
	local m31,m32,m33=0,0,1
	return x*m11+y*m12+z*m13,  x*m21+y*m22+z*m23,  x*m31+y*m32+z*m33
end
function ray.rotateV3(x,y,z,ry,rx)
	ry,rx=ray.normangle(ry),ray.normangle(rx)
	x,y,z=rotateV3Y(x,y,z,ry)
	x,y,z=rotateV3X(x,y,z,rx)
	x,y,z=rotateV3Z(x,y,z,0)
	return x,y,z
end

 

 

Кто нибудь может написать правильно вращающий эквивалент? Буду очень благодарен.

Изменено пользователем Alex
спойлер

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Рекомендуемые сообщения

Интересно, для чего тебе вращение трехмерных векторов при написании рейкастера, если соль алгоритма именно в минимизации векторных расчетов и статичной производительности? Но хозяин - барин. Во-первых, на кой дьявол постоянно использовать конверсию из градусов в радианы? Сожрет же львиную долю ресурсов ЦП, а толку ноль. Юзай сразу радианы, а в человекопонятный градусный формат выводи лишь в интерфейсе. Во-вторых, очень много локальных переменных - рендерер загнется от "out of memory" на сценах с удаленной от точки каста геометрией, т.к. GC не успеет подтереть кучки локального навоза. В-третьих, незачем столь часто обращаться к таблице math и вычислять значения синусов: достаточно разово передать их в расчетную функцию. Также разумнее использовать векторные координаты в качестве последних аргументов функций, чтобы более эффективно передавать результат расчетов из одной функции в другую, хотя лично я бы вообще ограничился тремя локальными переменными для всех векторов и работал бы только с ними. В-четвертых, в нормализации углов особого смысла нет, т.к. тригонометрические функции автоматом работают с углами любого формата. Разве что если ты захочешь сделать заранее вычисленную таблицу косинусов/синусов и получать значения углов уже из нее - вот тогда да, приводить углы к диапазону [-2π; +2π] уже разумно, хотя на практике прирост производительности толком не заметен.

 

 

-- На всякий, мало ли надо
local TWO_PI = 2 * math.pi
local function normalize(angle)
  angle = angle % TWO_PI
  return angle >= 0 and angle or angle + TWO_PI
end

local function rotateX(sin, cos, x, y, z)
  return
    x,
    cos * y - sin * z,
    sin * y + cos * z
end

local function rotateY(sin, cos, x, y, z)
  return
    cos * x + sin * z,
    y,
    cos * z - sin * x
end

local function rotateZ(sin, cos, x, y, z)
  return
    cos * x - sin * y,
    sin * x + cos * y,
    z
end

local function rotateXY(rx, ry, x, y, z)
  return rotateX(math.sin(rx), math.cos(rx), rotateY(math.sin(ry), math.cos(ry), x, y, z))
end

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Интересно, для чего тебе вращение трехмерных векторов при написании рейкастера, если соль алгоритма именно в минимизации векторных расчетов и статичной производительности?

А как без вращения 3D точек сделать 3D рэйкастер? Я не знаю как. Вот у меня вот такие функции отвечают за рэйкастинг:

 

 

function ray.ray(bd,sx,sy,sz,x,y,z,ax,ay,l,f)
	--assert(x>=sx or y>=sy or z>=sz,"out of bounds")
	l=l or 100
	f=(f==true) and true or false
	bgr,bgg,bgb=128,128,255
	local fog=f and 255/(l/4) or 0
	local r,g,b,a=0,0,0,0
	local p=0
	local ox,oy,oz=ray.rotateV3(0,0,1,ay,ax)
	local rl=0.01
	local rla=0.01
	local rl2=0
	while a<255 and p<l do
		--print("R",p,math.floor(x),math.floor(y),math.floor(z),"|",r,g,b,a)
		local br,bg,bb,ba=0,0,0,0
		do
			local x,y,z=math.floor(x),math.floor(y),math.floor(z)
			--if x < sx and x >= 0 and y < sy and y >= 0 and z < sz and z >= 0 then
				br,bg,bb,ba=ray.get(bd,x,y,z)
				if ba > 0 then				
				rl2=1
				end
			--end
		end
		do
			local _
			local x,y,z=math.floor(x+ox*0.5),math.floor(y+oy*0.5),math.floor(z+oz*0.5)
			local br,bg,bb,ba=0,0,0,0
			--if x < sx and x >= 0 and y < sy and y >= 0 and z < sz and z >= 0 then
			--	local pia=x*(sy*sz*4)+y*(sz*4)+z*4+1
			--	--print(pia>=#bd,pia,#bd)
				_,_,_,ba=ray.get(bd,x,y,z)
			--end
			if ba<=0 then rl2=0.5 end
		end
		r=r+br*(ba/255)+((l-l/4<=p) and fog*rl or 0)
		g=g+bg*(ba/255)+((l-l/4<=p) and fog*rl or 0)
		b=b+bb*(ba/255)+((l-l/4<=p) and fog*rl or 0)
		a=a+ba+((l-l/4<=p) and fog*rl or 0)
		p=p+rl
		x,y,z=x+ox*math.max(rl,rl2),y+oy*math.max(rl,rl2),z+oz*math.max(rl,rl2)
		rl=rl+rla
		rla=rla*1.01
		if rl2 > 0 then
			rl2=0
		end
	end
	return r,g,b,a
end
function ray.rayidata(idata,b,sx,sy,sz,f,vl,cx,cy,cz,ax,ay,py,px)
	local hwidth=idata:getWidth()/2
	local hheight=idata:getHeight()/2
	local py2=py/(idata:getWidth())
	local px2=px/(idata:getHeight())
	local function convert(x,y)
		local width=idata:getWidth()-1
		local height=idata:getHeight()-1
		return py2*(x)+ay,px2*(y)+ax
	end
	idata:mapPixel(function(x,y)
		local ay,ax=convert((x-hwidth),(y-hheight))
		--local ay2,ax2=convert(-(x-hwidth),-(y-hheight))
		--print((x-hwidth),(y-hheight),"|",ay-ay2,ax-ax2,"|",py2,px2)
		local r,g,b,a=ray.ray(b,sx,sy,sz,cx,cy,cz,ax,ay,vl,f)
		--print(x,y,"|",r,g,b,a,"|",(ax-hwidth)*py2,(ay-hheight)*px2)
		return r/255,g/255,b/255,a/255
	end)
end

 

 

 

Если что, то это все работает в love2d. Вот я поставил твои функции, но все равно как то не то. На рендеринг поставлен плоский мир, и он должен выглядеть соотвествующе, но как бы не так:

 

 

2018_06_12_14_30_20.png

 

 

Может, нужно использовать углы Эйлера или кватернионы?

Изменено пользователем Alex
сокращение цитаты, спойлер

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А как без вращения 3D точек сделать 3D рэйкастер?

 

Берешь FOV камеры, а также ее текущий поворот, например, по горизонтали. Через цикл изменяешь угол будущего луча в диапазоне [поворот камеры - FOV / 2; поворот камеры + FOV / 2] с заранее рассчитанными шагом, равным ширина экрана / FOV (чтобы число лучей было равным ширине экрана). Далее необходимо прокастовать луч, следующий вдоль полученного угла вплоть до желаемой дистанции прорисовки. Поскольку рейкастинг всегда выполняется с определенной точностью, то разумнее всего будет разово высчитать итеративное смещение кусочка луча по оси X через sin(угол луча) * точность и по оси Y через cos. Полученные значения необходимо раз за разом прибавлять к точке старта луча, чтобы усмешно достигнуть конца. Ну, а далее проще простого: пробегаешься циклом по всей длине луча, попутно вычисляя мировые координаты вокселя по горизонтали. А затем повторяешь вышеописанное для вертикальной ориентации, проверяя наличие вокселя в обоих плоскостях и делая с ним все, что только заблагорассудится.

 

Соль подобного подхода в том, что число тригонометрических операций будет эквивалентно ширина * высота экрана: и никаких тебе векторных вращений и бессмысленных расчетов. Цветами выделил инфу из этой пикчи:

 

QgL3iAu.png?1

 

Вот я поставил твои функции, но все равно как то не то

 

Значит, ищи ошибку. Я твой софт не кодил, и дебажить его не намерен. Формулы выше я скопипастил с собственного 3D-движка для опенкомпов, который с удовольствием их кушает. К слову, несмотря на наличие динамического освещения с полной перспективной коррекции, реализован он безо всяких углов Эйлера, матриц, кватернионов и прочей чуши на чистой линейной математике уровня 9 класса

 

Может, нужно использовать углы Эйлера или кватернионы?

 

Зачем, заче-е-ем? К чему этот геморрой, когда сам алгоритм проще пареной репы? Подобное еще на Сегу во времена Вульфа кодили, слухом не слыхивая о подобной терминологии в компьютерной графике. Ты точно рейкастинг имеешь в виду, а не полноценное трехмерное проецирование со всеми вытекающими?

Изменено пользователем ECS

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

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

Гость
Ответить на вопрос...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

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


×
×
  • Создать...