ваш DDA имеет две проблемы, которые я вижу по первому взгляду:
работают, только если Z является главной осью
, поэтому толькоесли вы находитесь в пространстве камеры или у вас стационарная камера, смотрящая в направлении Z
ваши дельты странные
почему:
delta? = abs(1 / look.Normalized().?);
Я бы ожидал:
delta? = abs(look.Normalized().?);
Я не кодирую в C # , поэтому я не уверен, что чиню ваш код, но вот мой шаблон C ++ дляn
-мерный DDA , так что просто сравните и восстановите ваш в соответствии с ним ...
template<const int n>class DDA
{
public:
int p0[n],p1[n],p[n];
int d[n],s[n],c[n],ix;
DDA(){};
DDA(DDA& a) { *this=a; }
~DDA(){};
DDA* operator = (const DDA *a) { *this=*a; return this; }
//DDA* operator = (const DDA &a) { ..copy... return this; }
void start()
{
int i;
for (ix=0,i=0;i<n;i++)
{
p[i]=p0[i]; s[i]= 0; d[i]=p1[i]-p0[i];
if (d[i]>0) s[i]=+1;
if (d[i]<0){ s[i]=-1; d[i]=-d[i]; }
if (d[ix]<d[i]) ix=i;
}
for (i=0;i<n;i++) c[i]=d[ix];
}
void start(double *fp0) // this will add the subpixel offset according to first point as double
{
int i; start();
for (i=0;i<n;i++)
{
if (s[i]<0) c[i]=double(double(d[ix])*( fp0[i]-floor(fp0[i])));
if (s[i]>0) c[i]=double(double(d[ix])*(1.0-fp0[i]+floor(fp0[i])));
}
}
bool update()
{
int i;
for (i=0;i<n;i++){ c[i]-=d[i]; if (c[i]<=0){ c[i]+=d[ix]; p[i]+=s[i]; }}
return (p[ix]!=p1[ix]+s[ix]);
}
};
start()
инициализируйте переменные и положение для DDA (от p0,p1
контрольных точек) и update()
- это всего лишь один шаг DDA ... Результирующая повторная точка находится в p
s
- это шаг, d
- это дельта, c
- счетчик и ix
- индекс главной оси.
Использование выглядит следующим образом:
DDA<3> A; // 3D
A.p0[...]=...; // set start point
A.p1[...]=...; // set end point
for (A.start();A.update();)
{
A.p[...]; // here use the iterated point
}
DDA проходит через
хорошо DDA просто интерпоРасстановка (растеризация) целочисленных позиций на некоторой линии между двумя конечными точками (p0
, p1
).Уравнение строки выглядит следующим образом:
p(t) = p0 + t*(p1-p0);
t = <0.0,1.0>
однако это включает в себя плавающую математику, и нам нужно целое число, чтобы мы могли переписать что-то вроде этого:
dp = p1-p0
D = max (|dp.x|,|dp.y|,|dp.z|,...)
p.x(i) = p0.x + (dp.x*i)/D
p.y(i) = p0.y + (dp.y*i)/D
p.z(i) = p0.z + (dp.z*i)/D
...
i = { 0,1,...D }
где i,D
соответствуетбольшая ось (та, где больше всего изменений)Если вы посмотрите поближе, мы используем *i/D
, что является медленной операцией, и нам обычно нужна скорость, поэтому мы можем переписать значение therm (используя тот факт, что i
переходит от 0 к D по порядку) в нечто подобное (только для оси xостальные будут одинаковыми с разными индексами ...):
p.x=p0.x; // start position
s.x=0; d.x=p1.x-p0.x; // step and abs delta
if (d.x>0) s.x=+1;
if (d.x<0){ s.x=-1; d.x=-d.x; }
D = max(d.x,d.y,d.z,...); // major axis abs delta
c.x=D; // counter for the iteration
for (i=0;i<D;i++)
{
c.x-=d.x; // update counter with axis abs delta
if (c.x<=0) // counter overflowed?
{
c.x+=D; // update counter with major axis abs delta
p.x+=s.x; // update axis by step
}
}
Теперь взглянем поближе на счетчик c
, к которому мы добавляем D
и вычитаем d.x
, который являетсяi/D
переписано в D
итераций.Все остальные оси рассчитываются таким же образом, вам просто нужно добавить счетчик c
step s
и abs delta d
для каждой оси ...
btw, если это поможет взглянуть на это:
объемный трассировщик обратного луча GLSL
, что (я предполагаю) то, что вы делаете, но реализовано в шейдере GLSL (см. Фрагмент кода)он не использует DDA , вместо этого он добавляет вектор направления единицы в начальную позицию до тех пор, пока не достигнет чего-либо или не достигнет конца воксельного пространства ...
кстати, исходя из:
точно так же, как ваша ссылка.
[Редактировать] неправильные попадания(предположил по вашим комментариям)
Скорее всего, это не имеет ничего общего с DDA .Это больше похоже на крайний случай, когда вы стоите прямо на пересечении ячеек, или у вас неправильно усеченная позиция, или вы неправильно отсортировали попадания.Я помню, у меня были проблемы с этим.Я получил очень странное решение в GLSL , см. Ссылку выше и посмотрите фрагмент кода.Ищите
// YZ plane voxels hits
непосредственно после кода приведения луча без "DDA".Он определяет, какая плоскость вокселя будет поражена. Я думаю, что вы должны сделать что-то подобное.Это было странно, так как в 2D DOOM (также по ссылкам выше) у меня не было таких проблем ... но это было связано с тем, что они решались с использованием другой математики (подходит только для 2D).
Код GLSLнезадолго до того, как приведение луча немного меняет положение, чтобы избежать краевых случаев.обратите внимание на floor
и ceil
, но моя работает на поплавках, так что для математики понадобится некоторая настройка.К счастью, я ремонтировал другой движок для литья лучей, основываясь на этом:
И решение состоит в том, чтобы компенсировать DDA по субпиксельной начальной позиции луча.Я обновил DDA код выше, новое использование:
DDA<3> A; // 3D
A.p0[...]=...; // set start point
A.p1[...]=...; // set end point
for (A.start(start_point_as_double[3]);A.update();)
{
A.p[...]; // here use the iterated point
}
Также на втором уроке убедитесь, что в вашем DDA c,d,s
являются целыми числами, если онивместо этого плавающие, это может вызвать проблемы, которые вы описываете тоже ...