Помогите пожалуйста, ковыряюсь с системой частиц в 3Д на шейдерах (Godot 4).
Никак не могу сделать вроде элементарную вещь - диффузное отражение. У меня есть следующие данные: частица, она летит по направлению Vector3(x,y,z), ударяется в препятствие, в точке ударения я знаю нормаль в виде произвольного вектора в направлении Vector3(x,y,z). Мне нужно развернуть направление движения частицы в рандомном направлении в соответствии с нормалью по полусфере образованной плоскостью нормали, вот так:
Как это сделать самым простым способом?
В 2Д всё это решалось так просто...
vec2 rotate(vec2 v, float a) { float s = sin(a); float c = cos(a); //либо так: lowp mat2 m = mat2(vec2(c,-s),vec2(s,c)); return vec2(v.x*c - v.y*s,v.x*s + v.y*c); //m * v; }
В 3Д у меня есть такой код старта частицы в рандомном направлении:
float angle_xz = rand_from_seed_m1_p1(alt_seed) * spread_rad_xz; float angle_yz = rand_from_seed_m1_p1(alt_seed) * spread_rad_yz; vec3 direction_xz = vec3(sin(angle_xz), 0.0, cos(angle_xz)); vec3 direction_yz = vec3(0.0, sin(angle_yz), cos(angle_yz)); vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z); vec3 direction_nrm = length(direction) > 0.0 ? normalize(direction) : vec3(0.0, 0.0, 1.0); // rotate spread to direction vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm); if (length(binormal) < 0.0001) { // direction is parallel to Y. Choose Z as the binormal. binormal = vec3(0.0, 0.0, 1.0); } binormal = normalize(binormal); vec3 normal = cross(binormal, direction_nrm); spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z; VELOCITY = spread_direction * speed;
- это работает, но не для всех случаев, тут я так понимаю захардкожено поведение поворота только вокруг двух осей, то есть не для произвольного вектора... Как модифицировать чтобы это работало для произвольного вектора, не понимаю.
Prescott
для диффузного отражения входящий угол не играет никакой роли. по определению диффузное отражение — это отражение, которое не зависит от угла падения. так вот чтобы сгенерить рандомное направление в полусфере, достаточно сгенерить направление в сфере и если оно оказалось "не в той" полусфере, то помножить его на -1.
чтобы сгенерить рандомное направление на сфере, проще всего сделать это в полярных координатах, для этого генеришь сначала угол ф как равномерно распределённый 0..2*pi, а потом cos theta, как равномерно распределённое число -1..1. заметь, не угол тета равномерно распределён, а его косинус.
вот тут рассказывается, как сгенерить случайную точку на сфере: https://mathworld.wolfram.com/SpherePointPicking.html (у них угол фи и тета поменяны местами). обрати внимание, что сам угол тета тебе ни в какой момент времени не нужен, достаточно сгенерить его косинус и из косинуса напрямую посчитать синус.
Suslik
Пущай сразу равномерное распределение делает
float goldenAngle = pi * (3.0f - sqrt(5.0f)); for(int i = 0; i < samples; i++) { float y = 1.0f - ((float)i / (samples - 1)) * 2.0f; float radii = sqrt(1.0f - y * y); float theta = goldenAngle * i; float x = cos(theta) * radii; float z = sin(theta) * radii; }
Спасибо за помощь!
Прикол что я блин находил всё это. И даже уже готовый код:
Внедрял его, получил странный результат, и начал пытаться делать по другому...
А странность результата в том, что первый отскок работает как надо, а на последующий, второй отскок, частица летит в направлении точки удара первого отскока (то есть тупо угол отражения = углу падения) частица возвращается туда, откуда летела, то есть будто её направление умножается на -1 (это и происходит в функции return v * sign(dot(v, n));)
И в итоге меня походу проблема с генерацией рандомных чисел или что-то вроде того?
На вход функции vec3 randomSpherePoint(vec3 rand) - надо подать 2 рандомных числа (rand.x, rand.y) в диапазоне -1..1, верно же?
Делаю так:
vec3 vec_random = vec3(rand_from_seed_m1_p1(alt_seed),rand_from_seed_m1_p1(alt_seed),0.0); vec3 new_dir = randomHemispherePoint(vec_random,COLLISION_NORMAL); VELOCITY = normalize(new_dir) * speed;
Нужно генерировать другой seed?
Prescott
> На вход функции vec3 randomSpherePoint(vec3 rand) - надо подать 2 рандомных
> числа (rand.x, rand.y) в диапазоне -1..1, верно же?
да, но их нужно подавать _разные_. каждый раз, когда ты генеришь рандом, его нужно генерить разный, а ты дважды используешь одно и то же рандомное число. обычно рандомное число генерится как хеш экранных координат ещё чего-то. вот это "что-то" — это обычно произвольное число. чтобы сгенерить два разных рандомных числа, достаточно это число взять разное:
float3 v0 = hash33(float3( screen_coord.xy, 0.0f)); float3 v1 = hash33( float3( screen_coord.xy, 1.0f));
в случае множественных отражений имеет смысл вместо константы использовать номер баунса.
Да, спасибо ещё раз, вроде заработало адекватно.
Подобрать эти произвольные числа для более-менее нормального результата оказалось не так просто.
Получилось самая бессмысленная и беспощадная система освещения на частицах:
300 000 точек, ну, зато все "фотоны" в 3Д и тени тоже 3Д. Нвидиа РТХ отдыхает!
Prescott
> Подобрать эти произвольные числа для более-менее нормального результата оказалось не так просто.
если у тебя нормальная хеш-функция, то конкретные значения чисел не должны играть никакой роли, достаточно чтобы они были побитово различны.
> Получилось самая бессмысленная и беспощадная система освещения на частицах:
топ, продолжай.
система освещения на частицах.
300 000 точек, ну, зато все "фотоны"
Частицы сделать крупнее в раз 5 или 8.
И всего 60 000.
И надо еще блюрить.
PS: У Suslika появился конкурент :)))
Suslik
> если у тебя нормальная хеш-функция, то конкретные значения чисел не должны
> играть никакой роли
Хэш функция стоковая:
К сожалению, я плохо понимаю что в ней происходит. Вроде какие-то побитовые сдвиги, но зачем они и почему, пока не изучал.
Suslik
> достаточно чтобы они были побитово различны.
Побитовая различность достигается тем, что на вход разные числа подаются?
Новые сиды генерировал так:
uint new_seed1 = alt_seed + pos_seed + uint(bounces*10+100); uint new_seed2 = alt_seed + pos_seed + uint(bounces*30+300) + base_number;
ronniko
> Частицы сделать крупнее в раз 5 или 8.
> И всего 60 000.
> И надо еще блюрить.
>
>
> PS: У Suslika появился конкурент :)))
Да, попробую поэкспериментировать с размерами/типом частиц, но блюрить не знаю как. Надо же блюрить именно 3Д "изображение", весь объём воздуха, а не 2Д... Типа наверное по схожей технике как делают Volumetric Fog. Но мне пока до этого далеко, так же как и до конкуренции с Suslik :)
Prescott
Это как рейтрейсинг с ограниченной скоростью света?
Aslan
> Это как рейтрейсинг с ограниченной скоростью света?
Да, скорость частиц можно регулировать, правда получится просто освещение с задержкой.
Prescott
Частицы исходят из источников света? Или обратно из камеры?
Aslan
Текущая реализация самая тупая и наивная - частицы (это Billboard quad mesh) исходят из источника света и рандомно отскакивают от поверхностей по нормальной полусфере, после каждого отскока у частицы на 25% отнимается альфа канал.
Prescott
Ааа, так частицу видно со стороны. Интересная идея!
Думаю, можно вместо рисования квадов писать пикселы в фреймбуффер уменьшенного размера и раздувать постобработкой, притом аккумулировать несколько кадров
Тема в архиве.