Как лучше всего написать воксельный движок на C с учетом производительности

Я - арматура в OpenGl, и поэтому я стараюсь изучать только современные OpenGl 4.x. После того, как я закончил базовые уроки (например, вращающиеся кубы), я решил попробовать создать программу на основе вокселей, работающую исключительно с кубами. Цели этой программы заключались в том, чтобы быть быстрым, использовать ограниченную мощность ЦП и память и быть динамичным, чтобы размер карты мог изменяться, и блоки будут отображаться только в том случае, если в массиве указано, что блок заполнен.

У меня есть один VBO с вершинами и индексами куба, построенного из треугольников. Вначале, если функция рендеринга, я говорю OpenGl, что шейдеры должны использовать, а затем связываю VBO, как только это завершится, я выполняю этот цикл

Draw Cube Loop:

//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);
        }
    }
} 

Cube.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");
}

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

Как можно улучшить этот код, чтобы сделать его более эффективным и в полной мере использовать современные возможности OpenGL, чтобы уменьшить накладные расходы?

PS Я не ищу книгу, инструмент или сторонний ресурс в качестве ответа. Я использовал выборку задних поверхностей и тест глубины OpenGL, чтобы попытаться улучшить скорость, однако они не принесли существенной разницы, это все еще занимает ~50 мс. для рендеринга кадра, и это слишком много для сетки вокселей 32*32*32.

Вот скриншот того, что я делаю:

IMG

А вот ссылка на полный код:

2 ответа

Это потому, что вы делаете это неправильно. Ты звонишь 32^3 раз какая-то функция DrawCube это слишком большие накладные расходы (особенно если это меняет матрицы). Скорее всего, это займет гораздо больше времени, чем сам рендеринг. Вы должны передать все рендеринг сразу, если это возможно, например, как массив текстур или VBO со всеми кубами.

Вы должны делать все вещи внутри шейдеров (даже кубы...).

Вы не указали, какую технику вы хотите использовать для рендеринга вашего объема. Здесь есть много вариантов, некоторые из которых обычно используются:

  • трассировка лучей
  • Поперечное сечение
  • Подповерхностное рассеяние

Ваши кубики прозрачные или сплошные? Если твердо, почему вы делаете 32^3 кубики вместо только видимых ~32^2? Есть способы, как выбрать только видимые кубы перед рендерингом...

Лучше всего было бы использовать трассировку лучей и рендеринг внутри шейдерного фрагмента (без кубовых сеток только внутри кубического теста). Но для начала проще реализовать VBO со всеми кубами внутри сетки. Вы также можете иметь только точки в VBO и испускать кубы в последнем геометрическом шейдере....

Вот некоторая коллекция моих связанных с ними QA, которые могут помочь с каждой из техник...

трассировка лучей

Объемная трассировка лучей на порядок проще, чем сетчатая трассировка лучей.

Поперечное сечение

Это также намного проще для объема и в 3D...

Если вам нужна начальная точка для GLSL, взгляните на это:

[Edit1] Пример GLSL

Что ж, мне удалось разрушить очень упрощенный пример объемной трассировки лучей GLSL без преломлений и отражений. Идея состоит в том, чтобы навести луч для каждого пикселя камеры в вершинном шейдере и проверить, какая ячейка сетки объема и сторона воксельного куба попали внутрь фрагментного шейдера. Чтобы передать объем, который я использовал GL_TEXTURE_3D без мипмапов и с GL_NEAREST за s,t,r, Вот как это выглядит:

Скриншот

Я инкапсулировал код на стороне процессора в этот код 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
//---------------------------------------------------------------------------

Объем инициируется и используется следующим образом:

// [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() оказывает вещи... Не забудьте позвонить gl_exit до выключения приложения.

Вот вершинный шейдер:

//------------------------------------------------------------------
#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);
    }
//------------------------------------------------------------------

И фрагмент:

//------------------------------------------------------------------
#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;
    }
//---------------------------------------------------------------------------

Как вы видите, он очень похож на Mesh Raytracer, который я связал выше (это было сделано из него). Трассировка лучей - это просто техника Doom, перенесенная в 3D.

Я использовал свой собственный движок и VCL, поэтому вам нужно перенести его в вашу среду (AnsiString загрузка / компиляция / связывание строк и шейдеров и list<>) для получения дополнительной информации см. простую ссылку GL... Также я смешиваю старый GL 1.0 и основной GLSL материал, который не рекомендуется (я хотел, чтобы он был как можно проще), поэтому вы должны конвертировать один Quad в VBO.

glsl_draw() требует, чтобы шейдеры были связаны и связаны уже где ShaderProgram это идентификатор шейдеров.

Объем сопоставлен с (0.0,0.0,0.0) в (1.0,1.0,1.0), Камера в виде прямой матрицы tm_eye, reper класс - только моя матрица преобразования 4x4, держащая оба прямых rep и обратный inv матрица что-то вроде GLM.

Разрешение громкости устанавливается в gl_init() жестко 32x32x32 так что просто поменяй строчку i=32 к тому, что вам нужно.

Код не оптимизирован и не подвергается тщательному тестированию, но выглядит так, как будто он работает. Времена на скриншоте говорят о многом, поскольку во время выполнения возникают огромные накладные расходы, так как я представляю это как часть более крупного приложения. Только tim значение более или менее надежно, но не сильно изменяется при больших разрешениях (вероятно, до тех пор, пока какое-то узкое место не будет достигнуто, например, размер памяти или разрешение экрана в зависимости от частоты кадров). Вот снимок экрана всего приложения (так что у вас есть представление о том, что еще работает):

IDE

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

Посмотрите документацию для glDrawElementsInstanced, однако этот подход также означает, что у вас должен быть "буфер" матриц, по одному для каждого воксельного куба, и вам нужно будет обращаться к каждой в шейдере, используя gl_InstanceID для индексации в правильной матрице.

Что касается буфера глубины, то при рендеринге будет сохранена экономия, если матрицы кубов будут каким-либо образом отсортированы спереди назад к камере, так что есть преимущество в производительности при неудачном тесте глубины на раннем этапе z для любого возможного фрагмента, который находится за уже Воксельный куб.

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