Для университетского задания я написал трассировщик лучей на 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 треугольников.
Этот текущий код отлично отображается:
Мне просто нужен способ обработки потенциально большого количества треугольников.
Однородные массивы очень малы, и в результате вы очень быстро достигнете этих пределов.
Использование текстуры — наиболее распространенный обходной путь, используемый для кодирования многих векторов/нормалей или других данных. Текстура обычно состоит из 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]?