#c #raytracing #embree
Вопрос:
Я пытаюсь обновить свое приложение с пересечения одного луча до пересечения потока.
Чего я не совсем понимаю, так это как возможно, чтобы gather
функции scatter
и, показанные в учебных пособиях, вообще работали
В примере определяется пользовательская структура расширенного луча Ray2
struct Ray2
{
Ray ray;
// ray extensions
float transparency; //!< accumulated transparency value
// we remember up to 16 hits to ignore duplicate hits
unsigned int firstHit, lastHit;
unsigned int hit_geomIDs[HIT_LIST_LENGTH];
unsigned int hit_primIDs[HIT_LIST_LENGTH];
};
затем он определяет массив этих Ray2
структур:
Ray2 primary_stream[TILE_SIZE_X*TILE_SIZE_Y];
этот массив задается как userRayExt перед вызовом метода пересечения:
primary_context.userRayExt = amp;primary_stream;
rtcIntersect1M(data.g_scene,amp;primary_context.context,(RTCRayHit*)amp;primary_stream,N,sizeof(Ray2));
теперь для каждого пучка лучей, который embree пересекает с геометрией, вызывается обратный вызов фильтра:
/* intersection filter function for streams of general packets */
void intersectionFilterN(const RTCFilterFunctionNArguments* args)
{
int* valid = args->valid;
const IntersectContext* context = (const IntersectContext*) args->context;
struct RTCRayHitN* rayN = (struct RTCRayHitN*)args->ray;
//struct RTCHitN* hitN = args->hit;
const unsigned int N = args->N;
/* avoid crashing when debug visualizations are used */
if (context == nullptr) return;
/* iterate over all rays in ray packet */
for (unsigned int ui=0; ui<N; ui =1)
{
/* calculate loop and execution mask */
unsigned int vi = ui 0;
if (vi>=N) continue;
/* ignore inactive rays */
if (valid[vi] != -1) continue;
/* read ray/hit from ray structure */
RTCRayHit rtc_ray = rtcGetRayHitFromRayHitN(rayN,N,ui);
Ray* ray = (Ray*)amp;rtc_ray;
/* calculate transparency */
Vec3fa h = ray->org ray->dir * ray->tfar;
float T = transparencyFunction(h);
/* ignore hit if completely transparent */
if (T >= 1.0f)
valid[vi] = 0;
/* otherwise accept hit and remember transparency */
else
{
/* decode ray IDs */
const unsigned int pid = ray->id / 1;
const unsigned int rid = ray->id % 1;
Ray2* ray2 = (Ray2*) context->userRayExt;
assert(ray2);
scatter(ray2->transparency,sizeof(Ray2),pid,rid,T);
}
}
}
the last line of this method is what I don’t understand
scatter(ray2->transparency,sizeof(Ray2),pid,rid,T);
I understand what it is SUPPOSED to do. It should update the transparency property of the Ray2
that corresponds to the traced ray with T. But I don’t get why/how this works, since the implementation of scatter
looks like this:
inline void scatter(floatamp; ptr, const unsigned int stride, const unsigned int pid, const unsigned int rid, float v) {
((float*)(((char*)amp;ptr) pid*stride))[rid] = v;
}
Я немного переформулирую эту функцию, чтобы лучше задать свой вопрос (но он должен быть полностью эквивалентен, если я не ошибаюсь).:
inline void scatter(floatamp; ptr, const unsigned int stride, const unsigned int pid, const unsigned int rid, float v) {
float* uptr = ((float*)(((char*)amp;ptr) pid*stride));
uptr[rid] = v;
}
Итак, первая строка все еще имеет для меня смысл. Указатель на поле прозрачности первой структуры Ray2 создается, а затем увеличивается на tid * sizeof(Ray2)
— это имеет смысл, так как он попадет в другое transparency
поле, так как он увеличивается на кратное sizeof(Ray2)
но затем следующая строка
uptr[rid] = v;
Я вообще ничего не понимаю. uptr
представляет собой указатель с плавающей точкой, указывающий на поле прозрачности. Таким образом , если rid
само по себе не кратно sizeof(Ray2)
, это вообще не будет указывать на поле прозрачности одного из лучей.
pid
и rid
рассчитываются как
const unsigned int pid = ray->id / 1;
const unsigned int rid = ray->id % 1;
что я нахожу странным. Разве это не всегда одно и то же, что
const unsigned int pid = ray->id;
const unsigned int rid = 0;
?
что такое pid
rid
и почему они вычисляются таким образом?
Ответ №1:
Не написав сам этот пример, трудно догадаться, каково было его первоначальное намерение, но я думаю, что ключ к разгадке кроется именно в вашем наблюдении, что для расчетов rid и pid деление/по модулю на » 1 » бессмысленно.
Итак, если rid eventially всегда заканчивается как » 0 » (потому что каждое значение mod 1 будет равно 0 :-/), то uptr[rid] = ...
это эквивалентно *uptr = ...
, что на самом деле правильно, так как вы сами указали, что uptr
всегда указывает на допустимую прозрачность.
Теперь о том, почему код делает эту запутанную вещь pid/rid? Если бы мне пришлось угадывать по названию «Ray2», я бы предположил, что в другой версии этого образца, возможно, использовались два луча и две прозрачные пленки в этой структуре ray2, а затем использовался параметр rid/pid, чтобы всегда выбирать правильный из пары.
Тем не менее, что касается первоначального вопроса «почему это вообще работает» : rid всегда принимает значение 0, поэтому он всегда записывает прямо в значение прозрачности, на которое uptr
указывает.