2016-09-26 277 views
1

我的最終目標是以60 fps渲染100萬個不同大小和顏色的球體。我希望能夠在屏幕上移動相機。在OpenGL中實現數百萬個對象的實例

我修改了this page of the tutorial I am studying上的代碼以嘗試實例一百萬個立方體。我已經能夠實例化多達90,000個立方體,但是如果我嘗試實例化160,000個立方體,那麼程序就會中斷。我收到程序「停止工作」並意外退出的錯誤。我不知道這是什麼樣的錯誤,但我相信它可能與內存有關。

我對實例的理解是天真的,所以我不知道問題是什麼。我相信實施100萬立方體是我實現100萬個領域的下一步目標。所以,我的問題:我如何在OpenGL中實例化一百萬個多維數據集/對象?

我已經通過this tutorial學習OpenGL和所以我用32-bit GLEW32-bit GLFW在Visual Studio 2013年我有8 GB的64位操作系統上RAM(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; 
} 

片段着色器:(名爲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

請在問題本身中包含源代碼。否則這個問題是無用的,當pastebins被刪除。 – BDL

+2

您正在將對象分配爲局部變量,因此在堆棧中,這不會佔用那麼多的對象。在堆上分配你的對象數組(你可以使用'std :: vector')。 –

回答

3

首先,我必須說,你Shader class是相機的代碼,但我也是從那個教程學習,因此僅僅通過改變自己吧。

您想要解決的問題與您的系統堆棧大小有關。在Visual Studio中,只允許你做一個局部變量的大小在1MB,並設定NUM_INS何時160000

真溶液(編輯)

像@Matteo意大利說,使用std::vector程序溢出相反,或者只是將您的陣列初始化部分glm::vec2 translations[NUM_INS];更改爲glm::vec2* translations = new glm::vec2[NUM_INS];,並且在您不使用時不要忘記delete。我測試第二種方式,它可以工作。對不起,我以前的錯誤答案,我應該更多地瞭解堆和堆棧!

對於誰不瞭解背景,我發現ref1ref2學習。


最壞的解決方案(以前,不宜用)

上懸而未決的問題,您可以通過以下步驟來更改在Visual Studio設置:

  1. 右擊你的項目 - >設置
  2. 轉到連接 - >系統
  3. 設置堆保留大小到(2M)

請注意,我的編輯是中國人,所以我不不知道細節的名字。通過設置此,您可以設置NUM_INS 16萬以上,看到的結果是這樣的:

+3

這是*堆棧*大小,並且增加它幾乎從來不是解決方案。 OP應該簡單地在堆上分配他的東西而不是堆棧。 –

+0

@Tokenyet謝謝你,你的評論幫助我解決了我的問題。我知道這是一個不同的問題,我應該把它作爲一個新的嗎?我的代碼與立方體運行在〜30 fps,但我預計它會更高。我能做些簡單的事情來加速它嗎? –

+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 MB,這會溢出堆棧(Windows上的1 MB使用默認鏈接器設置)。

的解決這個問題是增加堆棧大小:堆棧的尺寸有意地限制,並採取大的對象沒有被優化。相反,您應該在堆上分配您的元素,這樣您可以利用可用於您的流程的所有虛擬內存。

要做到這一點,無論是使用new/delete或 - 更簡單 - 學會使用std::vector類:

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

你的代碼的其餘部分將正常運行是。