Мне нужно отобразить много мелких объектов (размером 2 - 100 треугольников), которые лежат в глубокой иерархии, и каждый объект имеет свою собственную матрицу. Чтобы сделать их, я предварительно вычислил фактическую матрицу для каждого объекта, поместил объекты в один список, и у меня есть два вызова для рисования каждого объекта: установите равномерность матрицы и gl.drawElements().OpenGL ES (WebGL) рендеринг многих небольших объектов
Очевидно, что это не самый быстрый способ передвижения. Тогда у меня есть несколько тысяч объектов, которые становятся неприемлемыми. Единственное решение, о котором я думаю, - это пакетные несколько объектов в один буфер. Но делать это непросто, потому что каждый объект имеет свою собственную матрицу и помещает объект в общий буфер. Мне нужно преобразовать его вершины по матрице на CPU. Еще хуже то, что пользователь может перемещать любые объекты в любое время, и мне нужно снова пересчитать большие данные вершин (потому что пользователь может перемещать объект со множеством вложенных детей)
Итак, я ищу альтернативные подходы. А недавно обнаружили странные вершинные шейдеры 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 width. 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: Может быть, лучший подход заключается в том, чтобы хранить кватернион вместо углов и напрямую преобразовывать вершины?
Большое спасибо за ваши исследования! Было бы даже лучше, если бы вы добавили информацию о своем оборудовании и ограничены ли ваши результаты? PS: вы пытались сохранить кватернион и положение, в результате получилось всего 2 текстуры?PPS: Вы экспериментировали с параметрами с чередованием и без перемежения? – Rem
И добавьте требования к памяти для каждого метода, пожалуйста. Для новичков стало легко рассуждать о том, какой из них выбрать! – Rem