Перейти к публикации
Форум - ComputerCraft
  • 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
спойлер

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


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

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

  • 0

Интересно, для чего тебе вращение трехмерных векторов при написании рейкастера, если соль алгоритма именно в минимизации векторных расчетов и статичной производительности? Но хозяин - барин. Во-первых, на кой дьявол постоянно использовать конверсию из градусов в радианы? Сожрет же львиную долю ресурсов ЦП, а толку ноль. Юзай сразу радианы, а в человекопонятный градусный формат выводи лишь в интерфейсе. Во-вторых, очень много локальных переменных - рендерер загнется от "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

 

 

  • Like 4

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


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

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

А как без вращения 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
сокращение цитаты, спойлер

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


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

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

 

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

 

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

 

QgL3iAu.png?1

 

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

 

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

 

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

 

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

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

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


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

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас

×