2016-09-28 3 views
3

Моя конечная цель - отобразить 1 миллион сфер разных размеров и цветов со скоростью 60 кадров в секунду. Я хочу также иметь возможность перемещать камеру по экрану.Признание миллионов объектов в OpenGL: улучшение кадров в секунду

Я изменил код на this page of the tutorial I am studying, чтобы попробовать экземпляр многих сфер. Тем не менее, я нахожу, что всего в 64 сферах мои fps опускаются ниже 60, а в 900 сферах мои fps являются ничтожными. 4. Мое понимание инстанса наивно, но я считаю, что я должен получать больше кадров в секунду, чем это. 60 кадров в секунду должны быть достигнуты только с 64 сферами. Я считаю, что я каким-то образом заставляю CPU и GPU общаться чаще, чем нужно. Поэтому мой вопрос: как я могу создать так много объектов (в идеале миллионы), не вызывая падение fps (в идеале 60 кадров в секунду)?

Я вычисляю fps, вычисляя (10/time_elapsed) каждые 10 кадров, где time_elapsed - это время, прошедшее со времени последнего вызова fps. Я печатаю это, используя printf в строке 118 моего кода.

Я учусь OpenGL через this tutorial и поэтому я использую 32-bit GLEW и 32-bit GLFW в Visual Studio 2013. У меня есть 8 ГБ оперативной памяти на операционной системе 64-разрядной (Windows 7) с 2.30 GHz CPU.

Я пробовал кодировать свой собственный пример на основе учебника выше. Исходный код:

(установите строку # 2, чтобы указать количество сфер, которые должны быть установлены. Убедитесь, что строка # 2 имеет квадратный корень целого числа. Установите строку 4 как деталь сферы, го равно 0. большее число = более подробные)

// Make sure NUM_INS is a square number 
#define NUM_INS 1 

// Detail up to 4 is probably good enough 
#define SPHERE_DETAIL 4 

#include <vector> 

// GLEW 
#define GLEW_STATIC 
#include <GL/glew.h> 

// GLFW 
#include <GLFW/glfw3.h> 

// GL includes 
#include "Shader.h" 

// GLM Mathemtics 
#include <glm/glm.hpp> 
#include <glm/gtc/matrix_transform.hpp> 
#include <glm/gtc/type_ptr.hpp> 

// Properties 
GLuint screenWidth = 800, screenHeight = 600; 

// Function prototypes 
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); 
std::vector<GLfloat> create_sphere(int recursion); 

// The MAIN function, from here we start our application and run the Game loop 
int main() 
{ 
    // Init GLFW 
    glfwInit(); 
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 

    GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed 
    glfwMakeContextCurrent(window); 

    // Set the required callback functions 
    glfwSetKeyCallback(window, key_callback); 

    // Initialize GLEW to setup the OpenGL Function pointers 
    glewExperimental = GL_TRUE; 
    glewInit(); 

    // Define the viewport dimensions 
    glViewport(0, 0, screenWidth, screenHeight); 
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Comment to remove wireframe mode 

    // Setup OpenGL options 
    glEnable(GL_DEPTH_TEST); 

    // Setup and compile our shader(s) 
    Shader shader("core.vs", "core.frag"); 

    // Generate a list of 100 quad locations/translation-vectors 
    std::vector<glm::vec2> translations(NUM_INS); 
    //glm::vec2 translations[NUM_INS]; 
    int index = 0; 
    GLfloat offset = 1.0f/(float)sqrt(NUM_INS); 
    for (GLint y = -(float)sqrt(NUM_INS); y < (float)sqrt(NUM_INS); y += 2) 
    { 
    for (GLint x = -(float)sqrt(NUM_INS); x < (float)sqrt(NUM_INS); x += 2) 
    { 
     glm::vec2 translation; 
     translation.x = (GLfloat)x/(float)sqrt(NUM_INS) + offset; 
     translation.y = (GLfloat)y/(float)sqrt(NUM_INS) + offset; 
     translations[index++] = translation; 
    } 
    } 

    // Store instance data in an array buffer 
    GLuint instanceVBO; 
    glGenBuffers(1, &instanceVBO); 
    glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * NUM_INS, &translations[0], GL_STATIC_DRAW); 
    glBindBuffer(GL_ARRAY_BUFFER, 0); 

    // create 12 vertices of a icosahedron 
    std::vector<GLfloat> vv = create_sphere(SPHERE_DETAIL); 

    GLuint quadVAO, quadVBO; 
    glGenVertexArrays(1, &quadVAO); 
    glGenBuffers(1, &quadVBO); 
    glBindVertexArray(quadVAO); 
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO); 
    glBufferData(GL_ARRAY_BUFFER, vv.size() * sizeof(GLfloat), &vv[0], GL_STATIC_DRAW); 
    glEnableVertexAttribArray(0); 
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0); 
    glEnableVertexAttribArray(1); 
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat))); 
    // Also set instance data 
    glEnableVertexAttribArray(2); 
    glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); 
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0); 
    glBindBuffer(GL_ARRAY_BUFFER, 0); 
    glVertexAttribDivisor(2, 1); // Tell OpenGL this is an instanced vertex attribute. 
    glBindVertexArray(0); 

    // For printing frames-per-second 
    float counter = 0; 
    double get_time = 0; 
    double new_time; 

    // Game loop 
    while (!glfwWindowShouldClose(window)) 
    { 
    // Print fps by printing (number_of_frames/time_elapsed) 
    counter += 1; 
    if (counter > 10) { 
     counter -= 10; 
     new_time = glfwGetTime(); 
     printf("fps: %.2f ", (10/(new_time - get_time))); 
     get_time = new_time; 
    } 

    // Check and call events 
    glfwPollEvents(); 

    // Clear buffers 
    //glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

    // Draw 100 instanced quads 
    shader.Use(); 

    glm::mat4 model; 
    model = glm::rotate(model, 0.0f, glm::vec3(1.0f, 0.0f, 0.0f)); 
    // Camera/View transformation 
    glm::mat4 view; 
    GLfloat radius = 10.0f; 
    GLfloat camX = sin(glfwGetTime()) * radius; 
    GLfloat camZ = cos(glfwGetTime()) * radius; 
    view = glm::lookAt(glm::vec3(camX, 0.0f, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); 
    // Projection 
    glm::mat4 projection; 
    projection = glm::perspective(45.0f, (GLfloat)screenWidth/(GLfloat)screenHeight, 0.1f, 100.0f); 
    // Get the uniform locations 
    GLint modelLoc = glGetUniformLocation(shader.Program, "model"); 
    GLint viewLoc = glGetUniformLocation(shader.Program, "view"); 
    GLint projLoc = glGetUniformLocation(shader.Program, "projection"); 
    // Pass the matrices to the shader 
    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); 
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); 
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection)); 

    glBindVertexArray(quadVAO); 
    glDrawArraysInstanced(GL_TRIANGLES, 0, vv.size()/3, NUM_INS); // 100 triangles of 6 vertices each 
    glBindVertexArray(0); 

    // Swap the buffers 
    glfwSwapBuffers(window); 
    } 

    glfwTerminate(); 
    return 0; 
} 

// Is called whenever a key is pressed/released via GLFW 
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) 
{ 
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 
    glfwSetWindowShouldClose(window, GL_TRUE); 
} 

std::vector<GLfloat> add_color(std::vector<GLfloat> sphere) { 
    // Add color 
    std::vector<GLfloat> colored_sphere; 
    for (GLint i = 0; i < sphere.size(); i+=9) { 
    colored_sphere.push_back(sphere[i]); 
    colored_sphere.push_back(sphere[i+1]); 
    colored_sphere.push_back(sphere[i+2]); 
    colored_sphere.push_back(0.0f); 
    colored_sphere.push_back(0.0f); 
    colored_sphere.push_back(0.0f); 
    colored_sphere.push_back(sphere[i+3]); 
    colored_sphere.push_back(sphere[i+4]); 
    colored_sphere.push_back(sphere[i+5]); 
    colored_sphere.push_back(0.0f); 
    colored_sphere.push_back(0.0f); 
    colored_sphere.push_back(0.0f); 
    colored_sphere.push_back(sphere[i+6]); 
    colored_sphere.push_back(sphere[i+7]); 
    colored_sphere.push_back(sphere[i+8]); 
    colored_sphere.push_back(0.0f); 
    colored_sphere.push_back(0.0f); 
    colored_sphere.push_back(0.0f); 
    } 

    return colored_sphere; 
} 

std::vector<GLfloat> tesselate(std::vector<GLfloat> shape, int recursion) { 

    if (recursion > 0) { 
    std::vector<GLfloat> new_sphere = {}; 
    for (GLint i = 0; i < shape.size(); i += 9) { 

     // 1.902113 approximately 
     GLfloat radius = sqrt(1.0f + pow((1.0f + sqrt(5.0f))/2.0f, 2)); 

     // Every 9 points is a triangle. Take 1 triangle and turn it into 4 triangles. 
     GLfloat p_one[] = {shape[i], shape[i + 1], shape[i + 2]}; 
     GLfloat p_two[] = {shape[i + 3], shape[i + 4], shape[i + 5]}; 
     GLfloat p_thr[] = {shape[i + 6], shape[i + 7], shape[i + 8]}; 
     GLfloat p_one_two[] = { (p_one[0] + p_two[0])/2.0f, (p_one[1] + p_two[1])/2.0f, (p_one[2] + p_two[2])/2.0f }; 
     GLfloat p_one_thr[] = { (p_one[0] + p_thr[0])/2.0f, (p_one[1] + p_thr[1])/2.0f, (p_one[2] + p_thr[2])/2.0f }; 
     GLfloat p_two_thr[] = { (p_two[0] + p_thr[0])/2.0f, (p_two[1] + p_thr[1])/2.0f, (p_two[2] + p_thr[2])/2.0f }; 

     GLfloat r_one_two = sqrt((p_one_two[0]*p_one_two[0]) + (p_one_two[1]*p_one_two[1]) + (p_one_two[2]*p_one_two[2])); 
     GLfloat r_one_thr = sqrt((p_one_thr[0]*p_one_thr[0]) + (p_one_thr[1]*p_one_thr[1]) + (p_one_thr[2]*p_one_thr[2])); 
     GLfloat r_two_thr = sqrt((p_two_thr[0]*p_two_thr[0]) + (p_two_thr[1]*p_two_thr[1]) + (p_two_thr[2]*p_two_thr[2])); 

     GLfloat t_one_two[] = { radius * p_one_two[0]/r_one_two, radius * p_one_two[1]/r_one_two, radius * p_one_two[2]/r_one_two }; 
     GLfloat t_one_thr[] = { radius * p_one_thr[0]/r_one_thr, radius * p_one_thr[1]/r_one_thr, radius * p_one_thr[2]/r_one_thr }; 
     GLfloat t_two_thr[] = { radius * p_two_thr[0]/r_two_thr, radius * p_two_thr[1]/r_two_thr, radius * p_two_thr[2]/r_two_thr }; 

     // Triangle 1: 
     new_sphere.push_back(p_one[0]); 
     new_sphere.push_back(p_one[1]); 
     new_sphere.push_back(p_one[2]); 

     new_sphere.push_back(t_one_two[0]); 
     new_sphere.push_back(t_one_two[1]); 
     new_sphere.push_back(t_one_two[2]); 

     new_sphere.push_back(t_one_thr[0]); 
     new_sphere.push_back(t_one_thr[1]); 
     new_sphere.push_back(t_one_thr[2]); 

     // Triangle 2: 
     new_sphere.push_back(p_two[0]); 
     new_sphere.push_back(p_two[1]); 
     new_sphere.push_back(p_two[2]); 

     new_sphere.push_back(t_one_two[0]); 
     new_sphere.push_back(t_one_two[1]); 
     new_sphere.push_back(t_one_two[2]); 

     new_sphere.push_back(t_two_thr[0]); 
     new_sphere.push_back(t_two_thr[1]); 
     new_sphere.push_back(t_two_thr[2]); 

     // Triangle 3: 
     new_sphere.push_back(p_thr[0]); 
     new_sphere.push_back(p_thr[1]); 
     new_sphere.push_back(p_thr[2]); 

     new_sphere.push_back(t_one_thr[0]); 
     new_sphere.push_back(t_one_thr[1]); 
     new_sphere.push_back(t_one_thr[2]); 

     new_sphere.push_back(t_two_thr[0]); 
     new_sphere.push_back(t_two_thr[1]); 
     new_sphere.push_back(t_two_thr[2]); 

     // Center Triangle: 

     new_sphere.push_back(t_one_two[0]); 
     new_sphere.push_back(t_one_two[1]); 
     new_sphere.push_back(t_one_two[2]); 

     new_sphere.push_back(t_one_thr[0]); 
     new_sphere.push_back(t_one_thr[1]); 
     new_sphere.push_back(t_one_thr[2]); 

     new_sphere.push_back(t_two_thr[0]); 
     new_sphere.push_back(t_two_thr[1]); 
     new_sphere.push_back(t_two_thr[2]); 
    } 
    return tesselate(new_sphere, recursion - 1); 
    } 

    printf("number of vertices to be rendered: %d || ", shape.size()); 
    return shape; 
} 

std::vector<GLfloat> create_sphere(int recursion) { 

    // Define the starting icosahedron 
    GLfloat t_ = (1.0f + sqrt(5.0f))/2.0f; 

    std::vector<GLfloat> icosahedron = { 
    -1.0f, t_, 0.0f, -t_, 0.0f, 1.0f, 0.0f, 1.0f, t_, 
    -1.0f, t_, 0.0f, 0.0f, 1.0f, t_, 1.0f, t_, 0.0f, 
    -1.0f, t_, 0.0f, 1.0f, t_, 0.0f, 0.0f, 1.0f, -t_, 
    -1.0f, t_, 0.0f, 0.0f, 1.0f, -t_, -t_, 0.0f, -1.0f, 
    -1.0f, t_, 0.0f, -t_, 0.0f, -1.0f, -t_, 0.0f, 1.0f, 

    1.0f, t_, 0.0f, 0.0f, 1.0f, t_, t_, 0.0f, 1.0f, 
    0.0f, 1.0f, t_, -t_, 0.0f, 1.0f, 0.0f, -1.0f, t_, 
    -t_, 0.0f, 1.0f, -t_, 0.0f, -1.0f, -1.0f, -t_, 0.0f, 
    -t_, 0.0f, -1.0f, 0.0f, 1.0f, -t_, 0.0f, -1.0f, -t_, 
    0.0f, 1.0f, -t_, 1.0f, t_, 0.0f, t_, 0.0f, -1.0f, 

    1.0f, -t_, 0.0f, t_, 0.0f, 1.0f, 0.0f, -1.0f, t_, 
    1.0f, -t_, 0.0f, 0.0f, -1.0f, t_,-1.0f, -t_, 0.0f, 
    1.0f, -t_, 0.0f,-1.0f, -t_, 0.0f, 0.0f, -1.0f, -t_, 
    1.0f, -t_, 0.0f, 0.0f, -1.0f, -t_, t_, 0.0f, -1.0f, 
    1.0f, -t_, 0.0f, t_, 0.0f, -1.0f, t_, 0.0f, 1.0f, 

    0.0f, -1.0f, t_, t_, 0.0f, 1.0f, 0.0f, 1.0f, t_, 
    -1.0f, -t_, 0.0f, 0.0f, -1.0f, t_,-t_, 0.0f, 1.0f, 
    0.0f, -1.0f, -t_,-1.0f, -t_, 0.0f,-t_, 0.0f, -1.0f, 
    t_, 0.0f, -1.0f, 0.0f, -1.0f, -t_, 0.0f, 1.0f, -t_, 
    t_, 0.0f, 1.0f, t_, 0.0f, -1.0f, 1.0f, t_, 0.0f, 
    }; 

    // Tesselate the icososphere the number of times recursion 
    std::vector<GLfloat> colorless_sphere = tesselate(icosahedron, recursion); 

    // Add color and return 
    return add_color(colorless_sphere); 
} 

Vertex Shader:. (названный core.vs)

#version 330 core 
layout (location = 0) in vec3 position; 
layout (location = 1) in vec3 color; 
layout (location = 2) in vec2 offset; 

out vec3 fColor; 

uniform mat4 model; 
uniform mat4 view; 
uniform mat4 projection; 

void main() 
{ 
    gl_Position = projection * view * model * vec4(position.x + offset.x, position.y + offset.y, position.z, 1.0f); 
    fColor = color; 
} 

Фрагмент Shader: (названный core.frag)

#version 330 core 
in vec3 fColor; 
out vec4 color; 

void main() 
{ 
    color = vec4(fColor, 1.0f); 
} 

Shader класс: (названный Shader.h)

#ifndef SHADER_H 
#define SHADER_H 

#include <string> 
#include <fstream> 
#include <sstream> 
#include <iostream> 

#include <GL/glew.h> 

class Shader 
{ 
    public: 
    GLuint Program; 
    // Constructor generates the shader on the fly 
    Shader(const GLchar* vertexPath, const GLchar* fragmentPath) 
    { 
    // 1. Retrieve the vertex/fragment source code from filePath 
    std::string vertexCode; 
    std::string fragmentCode; 
    std::ifstream vShaderFile; 
    std::ifstream fShaderFile; 
    // ensures ifstream objects can throw exceptions: 
     vShaderFile.exceptions(std::ifstream::badbit); 
    fShaderFile.exceptions(std::ifstream::badbit); 
    try 
    { 
     // Open files 
     vShaderFile.open(vertexPath); 
     fShaderFile.open(fragmentPath); 
     std::stringstream vShaderStream, fShaderStream; 
     // Read file's buffer contents into streams 
     vShaderStream << vShaderFile.rdbuf(); 
     fShaderStream << fShaderFile.rdbuf(); 
     // close file handlers 
     vShaderFile.close(); 
     fShaderFile.close(); 
     // Convert stream into string 
     vertexCode = vShaderStream.str(); 
     fragmentCode = fShaderStream.str(); 
    } 
     catch (std::ifstream::failure e) 
     { 
     std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; 
     } 
     const GLchar* vShaderCode = vertexCode.c_str(); 
     const GLchar * fShaderCode = fragmentCode.c_str(); 
     // 2. Compile shaders 
     GLuint vertex, fragment; 
     GLint success; 
     GLchar infoLog[512]; 
     // Vertex Shader 
     vertex = glCreateShader(GL_VERTEX_SHADER); 
     glShaderSource(vertex, 1, &vShaderCode, NULL); 
     glCompileShader(vertex); 
     // Print compile errors if any 
     glGetShaderiv(vertex, GL_COMPILE_STATUS, &success); 
     if (!success) 
     { 
     glGetShaderInfoLog(vertex, 512, NULL, infoLog); 
     std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; 
     } 
     // Fragment Shader 
     fragment = glCreateShader(GL_FRAGMENT_SHADER); 
     glShaderSource(fragment, 1, &fShaderCode, NULL); 
     glCompileShader(fragment); 
     // Print compile errors if any 
     glGetShaderiv(fragment, GL_COMPILE_STATUS, &success); 
     if (!success) 
     { 
     glGetShaderInfoLog(fragment, 512, NULL, infoLog); 
     std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; 
     } 
     // Shader Program 
     this->Program = glCreateProgram(); 
     glAttachShader(this->Program, vertex); 
     glAttachShader(this->Program, fragment); 
     glLinkProgram(this->Program); 
     // Print linking errors if any 
     glGetProgramiv(this->Program, GL_LINK_STATUS, &success); 
     if (!success) 
     { 
     glGetProgramInfoLog(this->Program, 512, NULL, infoLog); 
     std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; 
     } 
     // Delete the shaders as they're linked into our program now and no longer necessery 
     glDeleteShader(vertex); 
     glDeleteShader(fragment); 

    } 
    // Uses the current shader 
    void Use() 
    { 
     glUseProgram(this->Program); 
    } 
}; 

#endif 
+0

, вероятно, несущественный, но вы могли бы вычислить '(float) sqrt (NUM_INS)' раз и навсегда (CPU скажет спасибо). –

+0

Это помогло создать рендеринг: http://ogldev.atspace.co.uk/www/tutorial33/tutorial33.html Его просто другой призыв к вызову, который сообщает, что gpu перерабатывает уже отправленную информацию. – Charlie

+0

Какой класс аппаратного обеспечения вы настраиваете из любопытства? Вы всегда можете воспользоваться возможностью, чтобы узнать о вычислительных шейдерах и косвенной ничьей :) Сгенерируйте все эти экземпляры полностью на GPU-стороне, и все будет летать. –

ответ

4

Моя конечная цель состоит в том, чтобы сделать 1 миллион сфер различных размеров и цветов при 60 кадрах в секунду.

Это необоснованное ожидание.

Предположим, что каждая сфера состоит из 50 треугольников. Kinda маленький для хорошей формы сферы, но позволяет предположить, что они такие маленькие.

1 миллион сфер на 50 трис на сферу составляет 50 миллионов треугольников за кадр. При 60 FPS это 3 млрд. треугольников в секунду.

Нет доступных в продаже графических процессоров для этого достаточно. И это всего лишь 50 треугольников; ваш 4x тесселлированный икосаэдр будет более треугольников.

Теперь да, рисунок 60 таких сфер составляет всего ~ 300 000 треугольников на кадр. Но даже это при 60 FPS составляет ~ 18 миллионов треугольников в секунду. Существует аппаратное обеспечение, которое может обрабатывать многие треугольники, но это очень ясно лот.И у вас определенно не собирается получать 1 миллион из них.

Это не связано с коммуникацией или нагрузкой на GPU/CPU. Вы просто бросаете больше работы на свой GPU, чем могли бы справиться. Возможно, вам удастся улучшить пару вещей здесь и там, но ничего не даст вам даже одной десятой того, что вы хотите.

По крайней мере, не с этим общим подходом.


Для вашего конкретного случая желая привлечь миллионы сфер, я хотел бы использовать трассировки лучей самозванцев, а не фактическую геометрию сфер. То есть вы рисуете квадроциклы, чьи позиции генерируются шейдером вершины (или геометрии). Вы генерируете квадрат на сферу, так что квад описывает сферу. Затем фрагментарный шейдер выполняет простой тест пересечения лучей, чтобы увидеть, попадает ли этот фрагмент (со стороны камеры) в сферу или нет. Если луч не попадает в сферу, вы отбрасываете фрагмент.

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

+0

Возможно, вы правы, что миллионы сфер слишком амбициозны. Тем не менее, вот видео, в котором полмиллиона сфер отображаются в OpenGL, где они перемещаются и накладываются тоже: https://www.youtube.com/watch?v=Tnc_IoXwH24 Возможно, мне нужно изменить свой проект, чтобы соответствовать вашим критериям , или ниже fps, как в этом видео. –

+0

@PaulTerwilliger: Вы читали подробности об этом видео? В частности, часть, где говорится: «Количество треугольников, используемых для каждой сферы, зависит от расстояния от зрителя». Это правила, выступающие в качестве инструмента. –

+0

Понятно, что вы не знаете, как использовать инстанс для разных деталей сфер? Не могу ли я отправить сферы с различными деталями на GPU, а затем отобразить некоторые или другие в зависимости от расстояния? –