c performance opengl optimization opengl-4

Cómo escribir mejor un motor de vóxel en C teniendo en cuenta el rendimiento



performance opengl (2)

Eso es porque haces esto de la manera incorrecta. Está llamando 32^3 veces a alguna función DrawCube que es demasiado grande (especialmente si cambia las matrices). Eso lleva más probablemente mucho más tiempo que el renderizado en sí. Debería pasar todos los elementos de renderizado de una vez si es posible, por ejemplo, como una matriz de textura o VBO con todos los cubos.

Debes hacer todas las cosas dentro de los sombreadores (incluso los cubos ...).

No especificó qué técnica desea utilizar para representar su volumen. Aquí hay muchas opciones, algunas que generalmente se usan:

  • trazado de rayos
  • Sección transversal
  • Dispersión por debajo de la superficie

¿Son sus cubos transparentes o sólidos? Si es sólido, ¿por qué representa 32^3 cubos en lugar de solo los visibles ~32^2 ? Hay formas de cómo seleccionar solo cubos visibles antes de renderizar ...

Mi mejor opción sería usar trazado de rayos y renderizado dentro del sombreador de fragmentos (sin mallas de cubo solo dentro de la prueba de cubo). Pero para empezar, lo más fácil de implementar sería usar VBO con todos los cubos dentro como malla. También puede tener solo puntos en el VBO y emitir cubos en el sombreador de geometría más tarde ...

Aquí una colección de QA relacionados que podría ayudar con cada una de las técnicas ...

trazado de rayos

  • Cubo LED: Dibujar esfera 3D en C / C ++ ignora las cosas GL 1.0 y se enfoca en la función sphere()
  • Dispersión atmosférica en GLSL (trazado de rayos de volumen analítico)
  • Raytrace a través de una malla 3D. Usaría esto simplemente elimine las cosas de malla e intersección con una simple transformación de coordenadas de cubo para obtener coordenadas de cubo en su matriz será mucho más rápido ...
  • Dispersión de subsuperficie SSS esto es para material semitransparente

El trazador de rayos de volumen es de magnitud más simple que el trazado de rayos de malla.

Sección transversal

  • Sección transversal 4D

Esto también es una magnitud más simple para el volumen y en 3D ...

Si necesita algún punto de partida para GLSL, eche un vistazo a esto:

  • Ejemplo de sombreadores GL + VAO / VBO + GLSL + simples completos en C ++

[Editar1] ejemplo GLSL

Bueno, me las arreglé para reventar un ejemplo muy simplificado de trazado de rayos volumétricos GLSL sin refracciones ni reflejos. La idea es emitir un rayo por cada píxel de cámara en el sombreador de vértices y probar qué celda de cuadrícula de volumen y el lado del cubo de vóxel golpeó dentro del sombreador de fragmentos . Para pasar el volumen utilicé GL_TEXTURE_3D sin mipmaps y con GL_NEAREST para s,t,r . Así es como esto luce:

Encapsulé el código del lado de la CPU en este código C ++ / VCL :

//--------------------------------------------------------------------------- //--- GLSL Raytrace system ver: 1.000 --------------------------------------- //--------------------------------------------------------------------------- #ifndef _raytrace_volume_h #define _raytrace_volume_h //--------------------------------------------------------------------------- const GLuint _empty_voxel=0x00000000; class volume { public: bool _init; // has been initiated ? GLuint txrvol; // volume texture at GPU side GLuint size,size2,size3;// volume size [voxel] and its powers GLuint ***data,*pdata; // volume 3D texture at CPU side reper eye; float aspect,focal_length; volume() { _init=false; txrvol=-1; size=0; data=NULL; aspect=1.0; focal_length=1.0; } volume(volume& a) { *this=a; } ~volume() { gl_exit(); } volume* operator = (const volume *a) { *this=*a; return this; } //volume* operator = (const volume &a) { ...copy... return this; } // init/exit void gl_init(); void gl_exit(); // render void gl_draw(); // for debug void glsl_draw(GLint ShaderProgram,List<AnsiString> &log); // geometry void beg(); void end(); void add_box(int x,int y,int z,int rx,int ry,int rz,GLuint col); void add_sphere(int x,int y,int z,int r,GLuint col); }; //--------------------------------------------------------------------------- void volume::gl_init() { if (_init) return; _init=true; int x,y,z; GLint i; glGetIntegerv(GL_MAX_TEXTURE_SIZE,&i); size=i; i=32; if (size>i) size=i; // force 32x32x32 resolution size2=size*size; size3=size*size2; pdata =new GLuint [size3]; data =new GLuint**[size]; for (z=0;z<size;z++){ data[z] =new GLuint* [size]; for (y=0;y<size;y++){ data[z][y]=pdata+(z*size2)+(y*size); }} glGenTextures(1,&txrvol); } //--------------------------------------------------------------------------- void volume::gl_exit() { if (!_init) return; _init=false; int x,y,z; glDeleteTextures(1,&txrvol); size=0; size2=0; size3=0; for (z=0;z<size;z++){ if (data[z]) delete[] data[z]; } if (data ) delete[] data; data =NULL; if (pdata ) delete[] pdata; pdata=NULL; } //--------------------------------------------------------------------------- void volume::gl_draw() { int x,y,z; float xx,yy,zz,voxel_size=1.0/float(size); reper rep; double v0[3],v1[3],v2[3],p[3],n[3],q[3],r,sz=0.5; glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glPerspective(2.0*atanxy(focal_length,1.0)*rad,1.0,0.1,100.0); glScalef(aspect,1.0,1.0); // glGetDoublev(GL_PROJECTION_MATRIX,per); glScalef(1.0,1.0,-1.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); rep=eye; rep.lpos_set(vector_ld(0.0,0.0,-focal_length)); rep.use_inv(); glLoadMatrixd(rep.inv); glBegin(GL_POINTS); for (zz=-0.0,z=0;z<size;z++,zz+=voxel_size) for (yy=-0.0,y=0;y<size;y++,yy+=voxel_size) for (xx=-0.0,x=0;x<size;x++,xx+=voxel_size) if (data[z][y][x]!=_empty_voxel) { glColor4ubv((BYTE*)(&data[z][y][x])); glVertex3f(xx,yy,zz); } glEnd(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } //--------------------------------------------------------------------------- void volume::glsl_draw(GLint ShaderProgram,List<AnsiString> &log) { GLint ix,i; GLfloat n[16]; AnsiString nam; const int txru_vol=0; // uniforms nam="aspect"; ix=glGetUniformLocation(ShaderProgram,nam.c_str()); if (ix<0) log.add(nam); else glUniform1f(ix,aspect); nam="focal_length"; ix=glGetUniformLocation(ShaderProgram,nam.c_str()); if (ix<0) log.add(nam); else glUniform1f(ix,focal_length); nam="vol_siz"; ix=glGetUniformLocation(ShaderProgram,nam.c_str()); if (ix<0) log.add(nam); else glUniform1i(ix,size); nam="vol_txr"; ix=glGetUniformLocation(ShaderProgram,nam.c_str()); if (ix<0) log.add(nam); else glUniform1i(ix,txru_vol); nam="tm_eye"; ix=glGetUniformLocation(ShaderProgram,nam.c_str()); if (ix<0) log.add(nam); else{ eye.use_rep(); for (int i=0;i<16;i++) n[i]=eye.rep[i]; glUniformMatrix4fv(ix,1,false,n); } glActiveTexture(GL_TEXTURE0+txru_vol); glEnable(GL_TEXTURE_3D); glBindTexture(GL_TEXTURE_3D,txrvol); // this should be a VBO glColor4f(1.0,1.0,1.0,1.0); glBegin(GL_QUADS); glVertex2f(-1.0,-1.0); glVertex2f(-1.0,+1.0); glVertex2f(+1.0,+1.0); glVertex2f(+1.0,-1.0); glEnd(); glActiveTexture(GL_TEXTURE0+txru_vol); glBindTexture(GL_TEXTURE_3D,0); glDisable(GL_TEXTURE_3D); } //--------------------------------------------------------------------------- void volume::beg() { if (!_init) return; for (int i=0;i<size3;i++) pdata[i]=_empty_voxel; } //--------------------------------------------------------------------------- void volume::end() { if (!_init) return; int z; // volume texture init glEnable(GL_TEXTURE_3D); glBindTexture(GL_TEXTURE_3D,txrvol); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE); glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, size, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pdata); glDisable(GL_TEXTURE_3D); } //--------------------------------------------------------------------------- void volume::add_box(int x0,int y0,int z0,int rx,int ry,int rz,GLuint col) { if (!_init) return; int x1,y1,z1,x,y,z; x1=x0+rx; x0-=rx; if (x0<0) x0=0; if (x1>=size) x1=size; y1=y0+ry; y0-=ry; if (y0<0) y0=0; if (y1>=size) y1=size; z1=z0+rz; z0-=rz; if (z0<0) z0=0; if (z1>=size) z1=size; for (z=z0;z<=z1;z++) for (y=y0;y<=y1;y++) for (x=x0;x<=x1;x++) data[z][y][x]=col; } //--------------------------------------------------------------------------- void volume::add_sphere(int cx,int cy,int cz,int r,GLuint col) { if (!_init) return; int x0,y0,z0,x1,y1,z1,x,y,z,xx,yy,zz,rr=r*r; x0=cx-r; x1=cx+r; if (x0<0) x0=0; if (x1>=size) x1=size; y0=cy-r; y1=cy+r; if (y0<0) y0=0; if (y1>=size) y1=size; z0=cz-r; z1=cz+r; if (z0<0) z0=0; if (z1>=size) z1=size; for (z=z0;z<=z1;z++) for (zz=z-cz,zz*=zz,y=y0;y<=y1;y++) for (yy=y-cy,yy*=yy,x=x0;x<=x1;x++) { xx=x-cx;xx*=xx; if (xx+yy+zz<=rr) data[z][y][x]=col; } } //--------------------------------------------------------------------------- #endif //---------------------------------------------------------------------------

El volumen se inicia y se usa así:

// [globals] volume vol; // [On init] // here init OpenGL and extentions (GLEW) // load/compile/link shaders // init of volume data vol.gl_init(); vol.beg(); vol.add_sphere(16,16,16,10,0x00FF8040); vol.add_sphere(23,16,16,8,0x004080FF); vol.add_box(16,24,16,2,6,2,0x0060FF60); vol.add_box(10,10,20,3,3,3,0x00FF2020); vol.add_box(20,10,10,3,3,3,0x002020FF); vol.end(); // this copies the CPU side volume array to 3D texture // [on render] // clear screen what ever // bind shader vol.glsl_draw(shader,log); // log is list of strings I use for errors you can ignore/remove it from code // unbind shader // add HUD or what ever // refresh buffers // [on exit] vol.gl_exit(); // free what ever you need to like GL,...

vol.glsl_draw() procesa las cosas ... No olvides llamar a gl_exit antes de cerrar la aplicación.

Aquí el sombreador de vértices:

//------------------------------------------------------------------ #version 420 core //------------------------------------------------------------------ uniform float aspect; uniform float focal_length; uniform mat4x4 tm_eye; layout(location=0) in vec2 pos; out smooth vec3 ray_pos; // ray start position out smooth vec3 ray_dir; // ray start direction //------------------------------------------------------------------ void main(void) { vec4 p; // perspective projection p=tm_eye*vec4(pos.x/aspect,pos.y,0.0,1.0); ray_pos=p.xyz; p-=tm_eye*vec4(0.0,0.0,-focal_length,1.0); ray_dir=normalize(p.xyz); gl_Position=vec4(pos,0.0,1.0); } //------------------------------------------------------------------

Y fragmento:

//------------------------------------------------------------------ #version 420 core //------------------------------------------------------------------ // Ray tracer ver: 1.000 //------------------------------------------------------------------ in smooth vec3 ray_pos; // ray start position in smooth vec3 ray_dir; // ray start direction uniform int vol_siz; // square texture x,y resolution size uniform sampler3D vol_txr; // scene mesh data texture out layout(location=0) vec4 frag_col; //--------------------------------------------------------------------------- void main(void) { const vec3 light_dir=normalize(vec3(0.1,0.1,-1.0)); const float light_amb=0.1; const float light_dif=0.5; const vec4 back_col=vec4(0.1,0.1,0.1,1.0); // background color const float _zero=1e-6; const vec4 _empty_voxel=vec4(0.0,0.0,0.0,0.0); vec4 col=back_col,c; const float n=vol_siz; const float _n=1.0/n; vec3 p,dp,dq,dir=normalize(ray_dir),nor=vec3(0.0,0.0,0.0),nnor=nor; float l=1e20,ll,dl; // Ray trace #define castray/ for (ll=length(p-ray_pos),dl=length(dp),p-=0.0*dp;;)/ {/ if (ll>l) break;/ if ((dp.x<-_zero)&&(p.x<0.0)) break;/ if ((dp.x>+_zero)&&(p.x>1.0)) break;/ if ((dp.y<-_zero)&&(p.y<0.0)) break;/ if ((dp.y>+_zero)&&(p.y>1.0)) break;/ if ((dp.z<-_zero)&&(p.z<0.0)) break;/ if ((dp.z>+_zero)&&(p.z>1.0)) break;/ if ((p.x>=0.0)&&(p.x<=1.0)/ &&(p.y>=0.0)&&(p.y<=1.0)/ &&(p.z>=0.0)&&(p.z<=1.0))/ {/ c=texture(vol_txr,p);/ if (c!=_empty_voxel){ col=c; l=ll; nor=nnor; break; }/ }/ p+=dp; ll+=dl;/ } // YZ plane voxels hits if (abs(dir.x)>_zero) { // compute start position aligned grid p=ray_pos; if (dir.x<0.0) { p+=dir*(((floor(p.x*n)-_zero)*_n)-ray_pos.x)/dir.x; nnor=vec3(+1.0,0.0,0.0); } if (dir.x>0.0) { p+=dir*((( ceil(p.x*n)+_zero)*_n)-ray_pos.x)/dir.x; nnor=vec3(-1.0,0.0,0.0); } // single voxel step dp=dir/abs(dir.x*n); // Ray trace castray; } // ZX plane voxels hits if (abs(dir.y)>_zero) { // compute start position aligned grid p=ray_pos; if (dir.y<0.0) { p+=dir*(((floor(p.y*n)-_zero)*_n)-ray_pos.y)/dir.y; nnor=vec3(0.0,+1.0,0.0); } if (dir.y>0.0) { p+=dir*((( ceil(p.y*n)+_zero)*_n)-ray_pos.y)/dir.y; nnor=vec3(0.0,-1.0,0.0); } // single voxel step dp=dir/abs(dir.y*n); // Ray trace castray; } // XY plane voxels hits if (abs(dir.z)>_zero) { // compute start position aligned grid p=ray_pos; if (dir.z<0.0) { p+=dir*(((floor(p.z*n)-_zero)*_n)-ray_pos.z)/dir.z; nnor=vec3(0.0,0.0,+1.0); } if (dir.z>0.0) { p+=dir*((( ceil(p.z*n)+_zero)*_n)-ray_pos.z)/dir.z; nnor=vec3(0.0,0.0,-1.0); } // single voxel step dp=dir/abs(dir.z*n); // Ray trace castray; } // final color and lighting output if (col!=back_col) col.rgb*=light_amb+light_dif*max(0.0,dot(light_dir,nor)); frag_col=col; } //---------------------------------------------------------------------------

Como puede ver, es muy similar al Mesh Raytracer que vinculé anteriormente (se hizo a partir de él). El rastreador es simplemente esta técnica Doom portada a 3D .

Utilicé mi propio motor y VCL, por lo que debe AnsiString a su entorno (cadenas AnsiString y carga / compilación / enlace de sombreadores y list<> ) para obtener más información, consulte el enlace GL ... simple. También mezclo cosas viejas de GL 1.0 y core GLSL, lo que no es recomendable (quería mantenerlo lo más simple posible), por lo que debería convertir el Quad individual a VBO .

glsl_draw() requiere que los sombreadores estén vinculados y enlazados ya donde ShaderProgram es la identificación de los sombreadores.

El volumen se asigna de (0.0,0.0,0.0) a (1.0,1.0,1.0) . La cámara está en forma de matriz directa tm_eye . La clase reper es solo la matriz de transformación 4x4 mía que contiene tanto la matriz directa como la matriz inv inversa, algo así como GLM .

La resolución del volumen se establece en gl_init() codificado a 32x32x32 así que simplemente cambie la línea i=32 a lo que necesita.

El código no está optimizado ni probado en gran medida, pero parece que funciona. Los tiempos en la captura de pantalla no dicen mucho, ya que hay una gran sobrecarga durante el tiempo de ejecución, ya que tengo esto como parte de una aplicación más grande. Solo el valor de tiempo es más o menos confiable, pero no cambia mucho con resoluciones más grandes (probablemente hasta que se alcance un cuello de botella, como el tamaño de la memoria o la resolución de la pantalla frente a la velocidad de fotogramas) Aquí captura de pantalla de toda la aplicación (para que tenga una idea de qué más Esta corriendo):

Soy una armadura en OpenGl y por esta razón busco aprender solo cosas modernas de OpenGl the 4.x. Una vez que completé los tutoriales básicos (por ejemplo, cubos rotativos), decidí que intentaría crear un programa basado en vóxel que tratara únicamente con cubos. El objetivo de este programa era ser rápido, usar potencia y memoria de CPU limitadas y ser dinámico para que el tamaño del mapa pueda cambiar y los bloques solo se dibujen si en la matriz dice que el bloque está lleno.

Tengo un VBO con los vértices e índices de un cubo construido a partir de triángulos. Al principio, si la función de renderización le digo a OpenGl los sombreadores que deben usar y luego unir el VBO una vez que esté completa, ejecuto este bucle

Dibujar bucle de cubo:

//The letter_max are the dimensions of the matrix created to store the voxel status in // The method I use for getting and setting entries in the map are very efficient so I have not included it in this example for(int z = -(z_max / 2); z < z_max - (z_max / 2); z++) { for(int y = -(y_max / 2); y < y_max - (y_max / 2); y++) { for(int x = -(x_max / 2); x < x_max - (x_max / 2); x++) { DrawCube(x, y, z); } } }

Cubo.c

#include "include/Project.h" void CreateCube() { const Vertex VERTICES[8] = { { { -.5f, -.5f, .5f, 1 }, { 0, 0, 1, 1 } }, { { -.5f, .5f, .5f, 1 }, { 1, 0, 0, 1 } }, { { .5f, .5f, .5f, 1 }, { 0, 1, 0, 1 } }, { { .5f, -.5f, .5f, 1 }, { 1, 1, 0, 1 } }, { { -.5f, -.5f, -.5f, 1 }, { 1, 1, 1, 1 } }, { { -.5f, .5f, -.5f, 1 }, { 1, 0, 0, 1 } }, { { .5f, .5f, -.5f, 1 }, { 1, 0, 1, 1 } }, { { .5f, -.5f, -.5f, 1 }, { 0, 0, 1, 1 } } }; const GLuint INDICES[36] = { 0,2,1, 0,3,2, 4,3,0, 4,7,3, 4,1,5, 4,0,1, 3,6,2, 3,7,6, 1,6,5, 1,2,6, 7,5,6, 7,4,5 }; ShaderIds[0] = glCreateProgram(); ExitOnGLError("ERROR: Could not create the shader program"); { ShaderIds[1] = LoadShader("FragmentShader.glsl", GL_FRAGMENT_SHADER); ShaderIds[2] = LoadShader("VertexShader.glsl", GL_VERTEX_SHADER); glAttachShader(ShaderIds[0], ShaderIds[1]); glAttachShader(ShaderIds[0], ShaderIds[2]); } glLinkProgram(ShaderIds[0]); ExitOnGLError("ERROR: Could not link the shader program"); ModelMatrixUniformLocation = glGetUniformLocation(ShaderIds[0], "ModelMatrix"); ViewMatrixUniformLocation = glGetUniformLocation(ShaderIds[0], "ViewMatrix"); ProjectionMatrixUniformLocation = glGetUniformLocation(ShaderIds[0], "ProjectionMatrix"); ExitOnGLError("ERROR: Could not get shader uniform locations"); glGenVertexArrays(1, &BufferIds[0]); ExitOnGLError("ERROR: Could not generate the VAO"); glBindVertexArray(BufferIds[0]); ExitOnGLError("ERROR: Could not bind the VAO"); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); ExitOnGLError("ERROR: Could not enable vertex attributes"); glGenBuffers(2, &BufferIds[1]); ExitOnGLError("ERROR: Could not generate the buffer objects"); glBindBuffer(GL_ARRAY_BUFFER, BufferIds[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES), VERTICES, GL_STATIC_DRAW); ExitOnGLError("ERROR: Could not bind the VBO to the VAO"); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(VERTICES[0]), (GLvoid*)0); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(VERTICES[0]), (GLvoid*)sizeof(VERTICES[0].Position)); ExitOnGLError("ERROR: Could not set VAO attributes"); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, BufferIds[2]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(INDICES), INDICES, GL_STATIC_DRAW); ExitOnGLError("ERROR: Could not bind the IBO to the VAO"); glBindVertexArray(0); } void DestroyCube() { glDetachShader(ShaderIds[0], ShaderIds[1]); glDetachShader(ShaderIds[0], ShaderIds[2]); glDeleteShader(ShaderIds[1]); glDeleteShader(ShaderIds[2]); glDeleteProgram(ShaderIds[0]); ExitOnGLError("ERROR: Could not destroy the shaders"); glDeleteBuffers(2, &BufferIds[1]); glDeleteVertexArrays(1, &BufferIds[0]); ExitOnGLError("ERROR: Could not destroy the buffer objects"); } void DrawCube(float x, float y, float z) { ModelMatrix = IDENTITY_MATRIX; TranslateMatrix(&ModelMatrix, x, y, z); TranslateMatrix(&ModelMatrix, MainCamera.x, MainCamera.y, MainCamera.z); glUniformMatrix4fv(ModelMatrixUniformLocation, 1, GL_FALSE, ModelMatrix.m); glUniformMatrix4fv(ViewMatrixUniformLocation, 1, GL_FALSE, ViewMatrix.m); ExitOnGLError("ERROR: Could not set the shader uniforms"); glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, (GLvoid*)0); ExitOnGLError("ERROR: Could not draw the cube"); }

El sombreador de vértices solo maneja la rotación y la transformación de los vértices y el sombreador de fragmentos solo se ocupa del color, no es costoso de ejecutar, por lo que no son el cuello de botella.

¿Cómo se puede mejorar este código para renderizar de manera más eficiente y aprovechar al máximo las características modernas de OpenGL para disminuir la sobrecarga?

PD: No estoy buscando un libro, una herramienta o un recurso fuera del sitio como respuesta. Utilicé el sacrificio de la cara posterior y la prueba de profundidad OpenGL para tratar de mejorar la velocidad, sin embargo, no han hecho una gran diferencia, todavía está tardando ~ 50 ms renderizar un cuadro y eso es demasiado para una cuadrícula de vóxel de 32 * 32 * 32.

Aquí captura de pantalla de lo que estoy haciendo:

Y aquí enlace al código completo:


Si realiza llamadas de extracción por separado e invoca la ejecución del sombreador para cada cubo específico, esto será una pérdida de rendimiento masiva. Definitivamente recomendaría la creación de instancias: de esta manera, su código puede tener una sola llamada de sorteo y todos los cubos se procesarán.

Busque documentación para glDrawElementsInstanced, sin embargo, este enfoque también significa que tendrá que tener un "buffer" de matrices, una para cada cubo de vóxel, y tendrá que acceder a cada una en el sombreador usando gl_InstanceID para indexar en la matriz correcta.

Con respecto al búfer de profundidad, habrá un ahorro en su renderizado si las matrices de cubos de alguna manera se ordenan de adelante hacia atrás de la cámara, por lo que existe el beneficio de rendimiento de una falla de prueba de profundidad de z temprana para cualquier posible fragmento que yace detrás de un cubo de voxel rendido.