2016-09-26 276 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); 

你的代码的其余部分将正常运行是。