2016-09-26 3 views
1

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

Я изменил код на this page of the tutorial I am studying, чтобы попробовать экземпляр 1 миллиона кубиков. Я смог экземпляр до 90 000 кубов, но если я попытаюсь выполнить экземпляр 160 000 кубов, программа разрывается. Я получаю сообщение об ошибке, что программа «перестала работать» и неожиданно ушла. Я не знаю, какая это ошибка, но я считаю, что это может быть связано с памятью.

Мое понимание инстанса наивно, поэтому я не знаю, в чем проблема. Я считаю, что создание 1 миллиона кубов - это следующий шаг к моей цели - создать 1 миллион сфер. Итак, мой вопрос: как я могу добавить 1 миллион кубов/объектов в OpenGL?

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

Мой код ниже:

(комплект строка # 2 будет число кубов быть инстанс Убедитесь, что линия # 2 имеет целых чисел квадратный корень.)

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

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


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

    // 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 
    glm::vec2 translations[NUM_INS]; 
    int index = 0; 
    GLfloat offset = 1.0f/sqrt(NUM_INS); 
    for (GLint y = -sqrt(NUM_INS); y < sqrt(NUM_INS); y += 2) 
    { 
     for (GLint x = -sqrt(NUM_INS); x < sqrt(NUM_INS); x += 2) 
     { 
      glm::vec2 translation; 
      translation.x = (GLfloat)x/sqrt(NUM_INS) + offset; 
      translation.y = (GLfloat)y/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); 

    // Generate quad VAO 
    GLfloat quadVertices[] = { 
     // Positions // Colors 
     -0.05f, 0.05f, 1.0f, 0.0f, 0.0f, 
     0.05f, -0.05f, 0.0f, 1.0f, 0.0f, 
     -0.05f, -0.05f, 0.0f, 0.0f, 1.0f, 

     -0.05f, 0.05f, 1.0f, 0.0f, 0.0f, 
     0.05f, -0.05f, 0.0f, 1.0f, 0.0f, 
     0.05f, 0.05f, 0.0f, 0.0f, 1.0f 
    }; 

    GLfloat vertices[] = { 
     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 1.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 1.0f, 1.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 0.0f, 1.0f, 

     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.0f, 0.0f, 1.0f, 
     0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 1.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.0f, 0.0f, 1.0f, 

     -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 1.0f, 1.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.0f, 0.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 

     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 1.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.0f, 0.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 

     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 1.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.0f, 0.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 

     -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 1.0f, 1.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 1.0f, 0.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), 0.0f, 0.0f, 0.0f, 
     -0.5f/sqrt(NUM_INS), 0.5f/sqrt(NUM_INS), -0.5f/sqrt(NUM_INS), 0.0f, 1.0f, 0.0f 
    }; 

    GLuint quadVAO, quadVBO; 
    glGenVertexArrays(1, &quadVAO); 
    glGenBuffers(1, &quadVBO); 
    glBindVertexArray(quadVAO); 
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, 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); 


    // Game loop 
    while (!glfwWindowShouldClose(window)) 
    { 
     // Check and call events 
     glfwPollEvents(); 

     // Clear buffers 
     glClearColor(0.03f, 0.03f, 0.03f, 1.0f); 
     glClear(GL_COLOR_BUFFER_BIT); 

     // Draw 100 instanced quads 
     shader.Use(); 
     glBindVertexArray(quadVAO); 
     glDrawArraysInstanced(GL_TRIANGLES, 0, 36, 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); 
} 

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; 

void main() 
{ 
    gl_Position = 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)

#pragma once 

// Std. Includes 
#include <vector> 

// GL Includes 
#include <GL/glew.h> 
#include <glm/glm.hpp> 
#include <glm/gtc/matrix_transform.hpp> 



// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods 
enum Camera_Movement { 
    FORWARD, 
    BACKWARD, 
    LEFT, 
    RIGHT 
}; 

// Default camera values 
const GLfloat YAW = -90.0f; 
const GLfloat PITCH = 0.0f; 
const GLfloat SPEED = 3.0f; 
const GLfloat SENSITIVTY = 0.25f; 
const GLfloat ZOOM = 45.0f; 


// An abstract camera class that processes input and calculates the corresponding Eular Angles, Vectors and Matrices for use in OpenGL 
class Camera 
{ 
public: 
    // Camera Attributes 
    glm::vec3 Position; 
    glm::vec3 Front; 
    glm::vec3 Up; 
    glm::vec3 Right; 
    glm::vec3 WorldUp; 
    // Eular Angles 
    GLfloat Yaw; 
    GLfloat Pitch; 
    // Camera options 
    GLfloat MovementSpeed; 
    GLfloat MouseSensitivity; 
    GLfloat Zoom; 

    // Constructor with vectors 
    Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), GLfloat yaw = YAW, GLfloat pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM) 
    { 
     this->Position = position; 
     this->WorldUp = up; 
     this->Yaw = yaw; 
     this->Pitch = pitch; 
     this->updateCameraVectors(); 
    } 
    // Constructor with scalar values 
    Camera(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw, GLfloat pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM) 
    { 
     this->Position = glm::vec3(posX, posY, posZ); 
     this->WorldUp = glm::vec3(upX, upY, upZ); 
     this->Yaw = yaw; 
     this->Pitch = pitch; 
     this->updateCameraVectors(); 
    } 

    // Returns the view matrix calculated using Eular Angles and the LookAt Matrix 
    glm::mat4 GetViewMatrix() 
    { 
     return glm::lookAt(this->Position, this->Position + this->Front, this->Up); 
    } 

    // Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems) 
    void ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime) 
    { 
     GLfloat velocity = this->MovementSpeed * deltaTime; 
     if (direction == FORWARD) 
      this->Position += this->Front * velocity; 
     if (direction == BACKWARD) 
      this->Position -= this->Front * velocity; 
     if (direction == LEFT) 
      this->Position -= this->Right * velocity; 
     if (direction == RIGHT) 
      this->Position += this->Right * velocity; 
    } 

    // Processes input received from a mouse input system. Expects the offset value in both the x and y direction. 
    void ProcessMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean constrainPitch = true) 
    { 
     xoffset *= this->MouseSensitivity; 
     yoffset *= this->MouseSensitivity; 

     this->Yaw += xoffset; 
     this->Pitch += yoffset; 

     // Make sure that when pitch is out of bounds, screen doesn't get flipped 
     if (constrainPitch) 
     { 
      if (this->Pitch > 89.0f) 
       this->Pitch = 89.0f; 
      if (this->Pitch < -89.0f) 
       this->Pitch = -89.0f; 
     } 

     // Update Front, Right and Up Vectors using the updated Eular angles 
     this->updateCameraVectors(); 
    } 

    // Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis 
    void ProcessMouseScroll(GLfloat yoffset) 
    { 
     if (this->Zoom >= 1.0f && this->Zoom <= 45.0f) 
      this->Zoom -= yoffset; 
     if (this->Zoom <= 1.0f) 
      this->Zoom = 1.0f; 
     if (this->Zoom >= 45.0f) 
      this->Zoom = 45.0f; 
    } 

private: 
    // Calculates the front vector from the Camera's (updated) Eular Angles 
    void updateCameraVectors() 
    { 
     // Calculate the new Front vector 
     glm::vec3 front; 
     front.x = cos(glm::radians(this->Yaw)) * cos(glm::radians(this->Pitch)); 
     front.y = sin(glm::radians(this->Pitch)); 
     front.z = sin(glm::radians(this->Yaw)) * cos(glm::radians(this->Pitch)); 
     this->Front = glm::normalize(front); 
     // Also re-calculate the Right and Up vector 
     this->Right = glm::normalize(glm::cross(this->Front, this->WorldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement. 
     this->Up = glm::normalize(glm::cross(this->Right, this->Front)); 
    } 
}; 
+3

Ницца и повторяемость от звуков вещей. Что говорит [отладчик] (https://en.wikipedia.org/wiki/Debugger)? – user4581301

+1

Пожалуйста, укажите исходный код в самом вопросе. В противном случае этот вопрос бесполезен, когда пастегины удаляются. – BDL

+2

Вы выделяете объекты как локальные переменные, поэтому в стеке, которые не сделаны для того, чтобы принимать эти многие объекты. Выделите массив объектов в куче (вы можете использовать 'std :: vector'). –

ответ

3

Во-первых, я должен сказать, что ваш Shader class это код камеры, но я также узнал из этого урока, так просто изменить его самостоятельно.

Проблема, которую вы хотите использовать, относится к размеру вашего системного стека. В Visual Studio, только позволила вам сделать локальную переменную размером в 1 Мбайт, и ваша программа переливается при установке NUM_INS на 160000.

Истинное решение (Edited) сказал

Как @Matteo Italia, используйте std::vector вместо этого, или просто измените часть инициализации массива glm::vec2 translations[NUM_INS]; на glm::vec2* translations = new glm::vec2[NUM_INS];, и не забудьте указать delete, когда вы не будете использовать его. Я проверяю второй способ, он может работать. Извините за мой предыдущий плохой ответ, я должен узнать больше о куче и стеке!

Для тех, кто не понимает фона, я нашел ref1, ref2 для обучения.


Худшие Решение (предыдущий, не следует использовать)

Чтобы SLOVE проблему, вы можете изменить визуальную настройку студии с помощью следующих шагов:

  1. правой кнопкой мыши на вашем проект ->установки
  2. перейти к линкер ->система
  3. установить кучи зарезервированный размер в (2М)

Обратите внимание, что мой редактор китайский, так что я не» t точно знать имя для детали. Установив это, вы могли бы установить NUM_INS до 160000 или более, и увидеть результат, как эти:

+3

Это размер * стека *, и увеличение его почти никогда не является решением. OP должен просто выделять свои вещи в куче вместо стека. –

+0

@Tokenyet Спасибо, ваш комментарий помог мне решить мою проблему. Я понимаю, что это другой вопрос, должен ли я опубликовать его как новый? Мой код с кубами работает со скоростью ~ 30 кадров в секунду, но я ожидал, что он будет выше. Есть ли что-то простое, что я могу сделать, чтобы ускорить его? –

+0

@Paul Terwilliger вы должны написать новое сообщение о том, как вы реализуете код счетчика fps и другие детали. Здесь я просто хочу вам что-то напомнить, проверьте ваш [vsync] (http://stackoverflow.com/questions/11312318/my-limited-fps-60) и другие ограничения. Если вы это сделали, вперед для нового сообщения :) – Tokenyet

2

Здесь

glm::vec2 translations[NUM_INS]; 

вы направляете свой массив позиций в стеке; теперь, пока NUM_INS относительно невелик, это не такая уж большая проблема, но когда вы начинаете «большие» числа (скажем, 100000), стек просто не может его взять.

Учитывая, что каждый glm::vec2 элемента выполнен из пары 32-битных поплавков (так, каждый vec2 8 байт), 160000 элементов принимают 1,28 МБ, что переполнение стека (1 МБ на Windows, с настройками линкера по умолчанию).

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

Чтобы сделать это, либо использовать new/delete или - еще проще - научиться использовать std::vector класс:

std::vector<glm::vec2> translations(NUM_INS); 

Остальная часть кода должна работать как есть.