最近在尝试使用OpenGL实现四叉树LOD,一个思路是动态修改Buffer的数据以实现动态绘制。
查找到可以用glMapBuffer或者glBufferSubData来修改数据,在此对使用方式做个记录。
1、使用glMapBuffer
glMapBuffer是获取Buffer的指针地址,注意,在通过指针修改Buffer后需要调用glUnmapBuffer进行提交。
const int N = 128 + 1;
const int layerNum = std::log2(N - 1);
int dynamicDraw();
int dynamicDraw()
{
auto window = initWindow();
Ogle::Shader* drawShader = new Ogle::Shader("./01_DynamicDraw.vert", "./01_DynamicDraw.frag");
float* vertices = new float[N * N * 3];
float sX = -N / 2.0;
float sZ = N / 2.0;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
vertices[i * N * 3 + j * 3] = sX;
vertices[i * N * 3 + j * 3 + 1] = 0.0;
vertices[i * N * 3 + j * 3 + 2] = sZ;
sX += 1.0;
}
sX = -N / 2.0;
sZ -= 1.0;
}
int layerGrid[8] = { 1,2,4,8,16,32,64,128 };
//1×1
int divide = (N - 1) / layerGrid[0];
unsigned int* layer_1 = new unsigned int[layerGrid[0] * layerGrid[0] * 6];
for (int i = 0; i < layerGrid[0]; i++)
{
for (int j = 0; j < layerGrid[0]; j++)
{
layer_1[i * layerGrid[0] * 6 + j * 6] = i * divide * N + j * divide;
layer_1[i * layerGrid[0] * 6 + j * 6 + 1] = divide + i * divide * N + j * divide;
layer_1[i * layerGrid[0] * 6 + j * 6 + 2] = divide * N + i * divide * N + j * divide;
layer_1[i * layerGrid[0] * 6 + j * 6 + 3] = divide + i * divide * N + j * divide;
layer_1[i * layerGrid[0] * 6 + j * 6 + 4] = divide * N + i * divide * N + j * divide;
layer_1[i * layerGrid[0] * 6 + j * 6 + 5] = divide * (N + 1) + i * divide * N + j * divide;
}
}
//2×2
divide = (N - 1) / layerGrid[1];
unsigned int* layer_2 = new unsigned int[layerGrid[1] * layerGrid[1] * 6];
for (int i = 0; i < layerGrid[1]; i++)
{
for (int j = 0; j < layerGrid[1]; j++)
{
layer_2[i * layerGrid[1] * 6 + j * 6] = i * divide * N + j * divide;
layer_2[i * layerGrid[1] * 6 + j * 6 + 1] = divide + i * divide * N + j * divide;
layer_2[i * layerGrid[1] * 6 + j * 6 + 2] = divide * N + i * divide * N + j * divide;
layer_2[i * layerGrid[1] * 6 + j * 6 + 3] = divide + i * divide * N + j * divide;
layer_2[i * layerGrid[1] * 6 + j * 6 + 4] = divide * N + i * divide * N + j * divide;
layer_2[i * layerGrid[1] * 6 + j * 6 + 5] = divide * (N + 1) + i * divide * N + j * divide;
}
}
//4×4
divide = (N - 1) / layerGrid[2];
unsigned int* layer_3 = new unsigned int[layerGrid[2] * layerGrid[2] * 6];
for (int i = 0; i < layerGrid[2]; i++)
{
for (int j = 0; j < layerGrid[2]; j++)
{
layer_3[i * layerGrid[2] * 6 + j * 6] = i * divide * N + j * divide;
layer_3[i * layerGrid[2] * 6 + j * 6 + 1] = divide + i * divide * N + j * divide;
layer_3[i * layerGrid[2] * 6 + j * 6 + 2] = divide * N + i * divide * N + j * divide;
layer_3[i * layerGrid[2] * 6 + j * 6 + 3] = divide + i * divide * N + j * divide;
layer_3[i * layerGrid[2] * 6 + j * 6 + 4] = divide * N + i * divide * N + j * divide;
layer_3[i * layerGrid[2] * 6 + j * 6 + 5] = divide * (N + 1) + i * divide * N + j * divide;
}
}
//8×8
divide = (N - 1) / layerGrid[3];
unsigned int* layer_4 = new unsigned int[layerGrid[3] * layerGrid[3] * 6];
for (int i = 0; i < layerGrid[3]; i++)
{
for (int j = 0; j < layerGrid[3]; j++)
{
layer_4[i * layerGrid[3] * 6 + j * 6] = i * divide * N + j * divide;
layer_4[i * layerGrid[3] * 6 + j * 6 + 1] = divide + i * divide * N + j * divide;
layer_4[i * layerGrid[3] * 6 + j * 6 + 2] = divide * N + i * divide * N + j * divide;
layer_4[i * layerGrid[3] * 6 + j * 6 + 3] = divide + i * divide * N + j * divide;
layer_4[i * layerGrid[3] * 6 + j * 6 + 4] = divide * N + i * divide * N + j * divide;
layer_4[i * layerGrid[3] * 6 + j * 6 + 5] = divide * (N + 1) + i * divide * N + j * divide;
}
}
//16×16
divide = (N - 1) / layerGrid[4];
unsigned int* layer_5 = new unsigned int[layerGrid[4] * layerGrid[4] * 6];
for (int i = 0; i < layerGrid[4]; i++)
{
for (int j = 0; j < layerGrid[4]; j++)
{
layer_5[i * layerGrid[4] * 6 + j * 6] = i * divide * N + j * divide;
layer_5[i * layerGrid[4] * 6 + j * 6 + 1] = divide + i * divide * N + j * divide;
layer_5[i * layerGrid[4] * 6 + j * 6 + 2] = divide * N + i * divide * N + j * divide;
layer_5[i * layerGrid[4] * 6 + j * 6 + 3] = divide + i * divide * N + j * divide;
layer_5[i * layerGrid[4] * 6 + j * 6 + 4] = divide * N + i * divide * N + j * divide;
layer_5[i * layerGrid[4] * 6 + j * 6 + 5] = divide * (N + 1) + i * divide * N + j * divide;
}
}
//32×32
divide = (N - 1) / layerGrid[5];
unsigned int* layer_6 = new unsigned int[layerGrid[5] * layerGrid[5] * 6];
for (int i = 0; i < layerGrid[5]; i++)
{
for (int j = 0; j < layerGrid[5]; j++)
{
layer_6[i * layerGrid[5] * 6 + j * 6] = i * divide * N + j * divide;
layer_6[i * layerGrid[5] * 6 + j * 6 + 1] = divide + i * divide * N + j * divide;
layer_6[i * layerGrid[5] * 6 + j * 6 + 2] = divide * N + i * divide * N + j * divide;
layer_6[i * layerGrid[5] * 6 + j * 6 + 3] = divide + i * divide * N + j * divide;
layer_6[i * layerGrid[5] * 6 + j * 6 + 4] = divide * N + i * divide * N + j * divide;
layer_6[i * layerGrid[5] * 6 + j * 6 + 5] = divide * (N + 1) + i * divide * N + j * divide;
}
}
//64×64
divide = (N - 1) / layerGrid[6];
unsigned int* layer_7 = new unsigned int[layerGrid[6] * layerGrid[6] * 6];
for (int i = 0; i < layerGrid[6]; i++)
{
for (int j = 0; j < layerGrid[6]; j++)
{
layer_7[i * layerGrid[6] * 6 + j * 6] = i * divide * N + j * divide;
layer_7[i * layerGrid[6] * 6 + j * 6 + 1] = divide + i * divide * N + j * divide;
layer_7[i * layerGrid[6] * 6 + j * 6 + 2] = divide * N + i * divide * N + j * divide;
layer_7[i * layerGrid[6] * 6 + j * 6 + 3] = divide + i * divide * N + j * divide;
layer_7[i * layerGrid[6] * 6 + j * 6 + 4] = divide * N + i * divide * N + j * divide;
layer_7[i * layerGrid[6] * 6 + j * 6 + 5] = divide * (N + 1) + i * divide * N + j * divide;
}
}
//128×128
divide = (N - 1) / layerGrid[7];
unsigned int* layer_8 = new unsigned int[layerGrid[7] * layerGrid[7] * 6];
for (int i = 0; i < layerGrid[7]; i++)
{
for (int j = 0; j < layerGrid[7]; j++)
{
layer_8[i * layerGrid[7] * 6 + j * 6] = i * divide * N + j * divide;
layer_8[i * layerGrid[7] * 6 + j * 6 + 1] = divide + i * divide * N + j * divide;
layer_8[i * layerGrid[7] * 6 + j * 6 + 2] = divide * N + i * divide * N + j * divide;
layer_8[i * layerGrid[7] * 6 + j * 6 + 3] = divide + i * divide * N + j * divide;
layer_8[i * layerGrid[7] * 6 + j * 6 + 4] = divide * N + i * divide * N + j * divide;
layer_8[i * layerGrid[7] * 6 + j * 6 + 5] = divide * (N + 1) + i * divide * N + j * divide;
}
}
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
int i = sizeof(&vertices);
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, N * N * 3 * 4, vertices, GL_STATIC_DRAW);
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (N - 1) * (N - 1) * 6 * 4, layer_8, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glEnable(GL_DEPTH_TEST);
while (!glfwWindowShouldClose(window))
{
float currentFrame = static_cast<float>(glfwGetTime());
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
processInput(window);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//同时清楚深度缓冲区
glUseProgram(drawShader->id);
glm::mat4 model = glm::mat4(1.0);
glm::mat4 view = glm::mat4(1.0);
glm::mat4 projection = glm::mat4(1.0);
view = camera.GetViewMatrix();
projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f);
glUniformMatrix4fv(glGetUniformLocation(drawShader->id, "model"), 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(glGetUniformLocation(drawShader->id, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(drawShader->id, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glBindVertexArray(VAO);
int mN = 0;
if (camera.Position.y < 2)
{
auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_8, 128 * 128 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
mN = 128;
}
else if (camera.Position.y >= 2 && camera.Position.y < 4)
{
auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_7, 64 * 64 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
mN = 64;
}
else if (camera.Position.y >= 4 && camera.Position.y < 6)
{
auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_6, 32 * 32 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
mN = 32;
}
else if (camera.Position.y >= 6 && camera.Position.y < 8)
{
auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_5, 16 * 16 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
mN = 16;
}
else if (camera.Position.y >= 8 && camera.Position.y < 10)
{
auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_4, 8 * 8 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
mN = 8;
}
else if (camera.Position.y >= 10 && camera.Position.y < 12)
{
auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_3, 4 * 4 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
mN = 4;
}
else if (camera.Position.y >= 12 && camera.Position.y < 14)
{
auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_2, 2 * 2 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
mN = 2;
}
else if (camera.Position.y >= 14 && camera.Position.y < 16)
{
auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_1, 1 * 1 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
mN = 1;
}
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, mN * mN * 6, GL_UNSIGNED_INT, (void*)0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glDeleteProgram(drawShader->id);
glfwTerminate();
return 0;
}
2、使用glBufferSubData
将上面代码如下面一一做修改。
else if (camera.Position.y >= 2 && camera.Position.y < 4)
{
/*auto p = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(p, layer_7, 64 * 64 * 6 * 4);
bool update = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);*/
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, 64 * 64 * 6 * 4, layer_7);
mN = 64;
}
注意:修改的内容量不可超过Buffer初始化时的大小(初始化时的大小由glBufferData的设置决定),也就是说不可在设置的最大大小的基础上增加数据。
运行效果:
随着视角拉远,网格逐渐减少。