OpenGL ES (WebGL) рендеринг множества мелких объектов
Мне нужно визуализировать множество маленьких объектов (размером от 2 до 100 треугольников), которые лежат в глубокой иерархии, и у каждого объекта есть своя собственная матрица. Для их рендеринга я предварительно вычисляю фактическую матрицу для каждого объекта, помещаю объекты в один список и у меня есть два вызова для рисования каждого объекта: set matrixiform и gl.drawElements ().
Очевидно, что это не самый быстрый путь. Тогда у меня несколько тысяч объектов производительности становится недопустимым. Единственное решение, о котором я думаю, - это объединить несколько объектов в один буфер. Но это нелегко сделать, потому что у каждого объекта есть своя собственная матрица, и для помещения объекта в общий буфер мне нужно преобразовать его вершины в матрицу на процессоре. Еще хуже проблема в том, что пользователь может перемещать любые объекты в любое время, и мне нужно снова пересчитать большие данные вершин (потому что пользователь может перемещать объекты со многими вложенными дочерними элементами)
Поэтому я ищу альтернативные подходы. И недавно нашел странные вершинные шейдеры в проекте onshape.com:
uniform mat4 uMVMatrix;
uniform mat3 uNMatrix;
uniform mat4 uPMatrix;
uniform vec3 uSpecular;
uniform float uOpacity;
uniform float uColorAmbientFactor; //Determines how much of the vertex-specified color to use in the ambient term
uniform float uColorDiffuseFactor; //Determines how much of the vertex-specified color to use in the diffuse term
uniform bool uApplyTranslucentAlphaToAll;
uniform float uTranslucentPassAlpha;
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoordinate;
attribute vec4 aVertexColor;
varying vec3 vPosition;
varying lowp vec3 vNormal;
varying mediump vec2 vTextureCoordinate;
varying lowp vec3 vAmbient;
varying lowp vec3 vDiffuse;
varying lowp vec3 vSpecular;
varying lowp float vOpacity;
attribute vec4 aOccurrenceId;
float unpackOccurrenceId() {
return aOccurrenceId.g * 65536.0 + aOccurrenceId.b * 256.0 + aOccurrenceId.a;
}
float unpackHashedBodyId() {
return aOccurrenceId.r;
}
#define USE_OCCURRENCE_TEXTURE 1
#ifdef USE_OCCURRENCE_TEXTURE
uniform sampler2D uOccurrenceDataTexture;
uniform float uOccurrenceTexelWidth;
uniform float uOccurrenceTexelHeight;
#define ELEMENTS_PER_OCCURRENCE 2.0
void getOccurrenceData(out vec4 occurrenceData[2]) {
// We will extract the occurrence data from the occurrence texture by converting the occurrence id to texture coordinates
// Convert the packed occurrenceId into a single number
float occurrenceId = unpackOccurrenceId();
// We first determine the row of the texture by dividing by the overall texture wid,th. Each occurrence
// has multiple rgba texture entries, so we need to account for each of those entries when determining the
// element's offset into the buffer.
float divided = (ELEMENTS_PER_OCCURRENCE * occurrenceId) * uOccurrenceTexelWidth;
float row = floor(divided);
vec2 coordinate;
// The actual coordinate lies between 0 and 1. We need to take care that coordinate lies on the texel
// center by offsetting the coordinate by a half texel.
coordinate.t = (0.5 + row) * uOccurrenceTexelHeight;
// Figure out the width of one texel in texture space
// Since we've already done the texture width division, we can figure out the horizontal coordinate
// by adding a half-texel width to the remainder
coordinate.s = (divided - row) + 0.5 * uOccurrenceTexelWidth;
occurrenceData[0] = texture2D(uOccurrenceDataTexture, coordinate);
// The second piece of texture data will lie in the adjacent column
coordinate.s += uOccurrenceTexelWidth;
occurrenceData[1] = texture2D(uOccurrenceDataTexture, coordinate);
}
#else
attribute vec4 aOccurrenceData0;
attribute vec4 aOccurrenceData1;
void getOccurrenceData(out vec4 occurrenceData[2]) {
occurrenceData[0] = aOccurrenceData0;
occurrenceData[1] = aOccurrenceData1;
}
#endif
/**
* Create a model matrix from the given occurrence data.
*
* The method for deriving the rotation matrix from the euler angles is based on this publication:
* http://www.soi.city.ac.uk/~sbbh653/publications/euler.pdf
*/
mat4 createModelTransformationFromOccurrenceData(vec4 occurrenceData[2]) {
float cx = cos(occurrenceData[0].x);
float sx = sin(occurrenceData[0].x);
float cy = cos(occurrenceData[0].y);
float sy = sin(occurrenceData[0].y);
float cz = cos(occurrenceData[0].z);
float sz = sin(occurrenceData[0].z);
mat4 modelMatrix = mat4(1.0);
float scale = occurrenceData[0][3];
modelMatrix[0][0] = (cy * cz) * scale;
modelMatrix[0][1] = (cy * sz) * scale;
modelMatrix[0][2] = -sy * scale;
modelMatrix[1][0] = (sx * sy * cz - cx * sz) * scale;
modelMatrix[1][1] = (sx * sy * sz + cx * cz) * scale;
modelMatrix[1][2] = (sx * cy) * scale;
modelMatrix[2][0] = (cx * sy * cz + sx * sz) * scale;
modelMatrix[2][1] = (cx * sy * sz - sx * cz) * scale;
modelMatrix[2][2] = (cx * cy) * scale;
modelMatrix[3].xyz = occurrenceData[1].xyz;
return modelMatrix;
}
void main(void) {
vec4 occurrenceData[2];
getOccurrenceData(occurrenceData);
mat4 modelMatrix = createModelTransformationFromOccurrenceData(occurrenceData);
mat3 normalMatrix = mat3(modelMatrix);
vec4 position = uMVMatrix * modelMatrix * vec4(aVertexPosition, 1.0);
vPosition = position.xyz;
vNormal = uNMatrix * normalMatrix * aVertexNormal;
vTextureCoordinate = aTextureCoordinate;
vAmbient = uColorAmbientFactor * aVertexColor.rgb;
vDiffuse = uColorDiffuseFactor * aVertexColor.rgb;
vSpecular = uSpecular;
vOpacity = uApplyTranslucentAlphaToAll ? (min(uTranslucentPassAlpha, aVertexColor.a)) : aVertexColor.a;
gl_Position = uPMatrix * position;
}
Похоже, что они кодируют положение объекта и углы поворота как 2 записи в 4-компонентной текстуре с плавающей запятой, добавляют атрибут, который сохраняет положение каждого вершинного преобразования в этой текстуре, а затем выполняют матричное вычисление в вершинном шейдере.
Итак, вопрос в том, действительно ли этот шейдер является эффективным решением моей проблемы, или мне лучше использовать пакетную обработку или что-то еще?
PS: может быть, даже лучший подход - хранить кватернионы вместо углов и напрямую преобразовывать их?