Как я могу отправить большие массивы объектов во фрагментный шейдер с помощью WebGL2?

Для университетского задания я написал трассировщик лучей на JavaScript, используя Canvas с 2D-контекстом. Чтобы сделать это быстрее, я конвертирую его из 2D-контекста в контекст WebGL2.

В исходном коде для работы с сетками я храню данные треугольников (положения вершин, нормали вершин) в большом массиве, а данные сетки (первый индекс триплета, количество тригонов, данные материала) — в другом. Затем я просматриваю массивы во время рендеринга, чтобы вычислить пересечения лучей.

Я хотел бы иметь возможность сделать то же самое в своей новой реализации, но я не смог осмыслить это, поскольку существует ограничение на количество элементов, которые может содержать массив Uniform.

Есть ли способ отправить такие большие массивы объектов во фрагментный шейдер?

В настоящее время я просто использую однородный массив небольшого размера для хранения данных треугольников. Я еще не добавил сетки в свою реализацию WebGL.

Я определяю свои массивы во фрагментном шейдере следующим образом:

uniform int NumSpheres;
uniform Sphere Spheres[MAX_SPHERES];

uniform int NumTris;
uniform Triangle Triangles[MAX_TRIS];

И рассчитайте пересечения лучей с помощью этой функции:

HitInfo TraceRay(Ray ray) 
{
    HitInfo closestHit;
    closestHit.dst = 1000000000000.0;

    for (int i = 0; i < MAX_SPHERES; i++)
    {
        if (i == NumSpheres) break;

        Sphere sphere = Spheres[i];
        HitInfo hitInfo = RaySphere(ray, sphere.position, sphere.radius);

        if (hitInfo.didHit && hitInfo.dst < closestHit.dst) 
        {
            closestHit = hitInfo;
            closestHit.material = sphere.material;
        }
    }

    for (int i = 0; i < MAX_TRIS; i++) 
    {
        if (i == NumTris) break;
        
        Triangle triangle = Triangles[i];
        HitInfo hitInfo = RayTriangle(ray, triangle);
        if (hitInfo.didHit && hitInfo.dst < closestHit.dst) {
            closestHit = hitInfo;
            closestHit.material.colour = vec3(1, 1, 1);
        }
    }

    return closestHit;
}

В данный момент я заполняю однородный массив треугольников этим кодом:

let trianglesLocations = [];
for (let i = 0; i < triangles.length; i++) {
    trianglesLocations.push({
        posA: gl.getUniformLocation(raytraceProgram, `Triangles[${i}].posA`),
        posB: gl.getUniformLocation(raytraceProgram, `Triangles[${i}].posB`),
        posC: gl.getUniformLocation(raytraceProgram, `Triangles[${i}].posC`),
        normalA: gl.getUniformLocation(raytraceProgram, `Triangles[${i}].normalA`),
        normalB: gl.getUniformLocation(raytraceProgram, `Triangles[${i}].normalB`),
        normalC: gl.getUniformLocation(raytraceProgram, `Triangles[${i}].normalC`),
    });
}

function setTriangles() {
    let numTrisLocation = gl.getUniformLocation(raytraceProgram, "NumTris");
    gl.uniform1i(numTrisLocation, triangles.length);
    for (let i = 0; i < triangles.length; i++) {
        gl.uniform3fv(trianglesLocations[i].posA, [triangles[i].posA.x, triangles[i].posA.y, triangles[i].posA.z]);
        gl.uniform3fv(trianglesLocations[i].posB, [triangles[i].posB.x, triangles[i].posB.y, triangles[i].posB.z]);
        gl.uniform3fv(trianglesLocations[i].posC, [triangles[i].posC.x, triangles[i].posC.y, triangles[i].posC.z]);
        gl.uniform3fv(trianglesLocations[i].normalA, [triangles[i].normalA.x, triangles[i].normalA.y, triangles[i].normalA.z]);
        gl.uniform3fv(trianglesLocations[i].normalB, [triangles[i].normalB.x, triangles[i].normalB.y, triangles[i].normalB.z]);
        gl.uniform3fv(trianglesLocations[i].normalC, [triangles[i].normalC.x, triangles[i].normalC.y, triangles[i].normalC.z]);
    }
}

Но ограничение на размер однородного массива означает, что я не могу использовать этот метод для рендеринга нескольких сеток, каждая из которых может содержать >200 треугольников.

Этот текущий код отлично отображается:

Результат рендеринга

Мне просто нужен способ обработки потенциально большого количества треугольников.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Однородные массивы очень малы, и в результате вы очень быстро достигнете этих пределов.

Использование текстуры — наиболее распространенный обходной путь, используемый для кодирования многих векторов/нормалей или других данных. Текстура обычно состоит из 3 (RGB) или 4 компонентов (RGBA) и позволяет хранить 3 или 4 значения от 0 до 255. Этот диапазон значений накладывает очень строгие ограничения на данные, которые можно передавать с помощью текстуры. 0-255 может быть достаточно для нормального кодирования (например, для рельефного отображения), но этого недостаточно для координат.

Чтобы преодолеть это ограничение, вы можете использовать 2 байта на одно значение. Это даст вам диапазон значений от 0 до 65535, но займет в два раза больше места на текстуре. При таком подходе вам придется упаковать x, y и z, например, в 6 байт, а это займет 1,5 RGBA пикселя текстуры. Для текстуры 4096x4096 такой подход позволит хранить 2 796 202 вершины, а это очень много! Затем вы можете использовать униформы для отправки дополнительных сведений, например, сколько вершин или треугольников передается с текстурой или других дополнительных данных. Вы даже можете использовать один RGBA для упаковки 32-битного (4-байтового) значения, и вы все равно сможете упаковать 1 398 101 вершину, что очень много!

Единственным недостатком является то, что вам придется распаковывать эти значения из RGBA в одно 32-битное число путем умножения/сдвига компонентов, что может быть медленным при большом количестве вершин.

Пожалуйста, дайте мне знать, если это поможет.

Извините, если это глупый вопрос, но как я могу получить доступ к определенному значению в этой текстуре, аналогично тому, как вы можете это сделать с массивом data[index]?

Killjoy 23.03.2024 15:00

Нвм, этот ответ объясняет, как получить значения текселей из индекса.

Killjoy 23.03.2024 16:13

Другие вопросы по теме