问题描述
我正在尝试构建一个体素引擎,为此我必须创建数十万个体素,我希望我可以使用实例化渲染。然而,绘制是非常出乎意料的。我主要关注LearnOpenGL guide。
单独渲染每个体素时,程序运行良好:
但是,当使用实例化渲染时...
另一个角度...
我正在尝试将体素渲染成大块,所以我的代码如下所示:
体素.hpp
#pragma once
#include <stdio.h>
#include <iostream>
#include <vector>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using std::vector;
using glm::mat4;
using glm::vec3;
class Voxel {
float radius;
bool visible;
vec3 centerPoint;
public:
unsigned int VBO,VAO,EBO;
Voxel(vec3 center,float size,bool vis = false,bool single = false);
void setMVP(mat4 mvp);
void setVisible(bool v);
void generateElement();
};
体素.cpp
#include "voxel.hpp"
Voxel::Voxel(vec3 center,bool vis,bool single) {
visible = vis;
centerPoint = center;
radius = size;
generateElement();
}
void Voxel::setVisible(bool v) {
visible = v;
}
void Voxel::generateElement() {
// this time we need all 8 vertices and a length 36 index array
vec3 maxV(centerPoint.x + radius,centerPoint.y + radius,centerPoint.z + radius);
vec3 minV(centerPoint.x - radius,centerPoint.y - radius,centerPoint.z - radius);
float vertices[24] = {
maxV.x,maxV.y,maxV.z,maxV.x,minV.z,minV.y,minV.x,};
unsigned int indices[36] = {
0,2,1,// maxV.x
0,3,6,// minV.z
2,7,4,// minV.y
2,5,// minV.x
4,// maxV.y
1,// maxV.z
0,};
// for individual rendering there would be shader code here
glGenVertexArrays(1,&VAO);
glGenBuffers(1,&VBO);
glGenBuffers(1,&EBO);
glBindVertexArray(VAO);
// load data into vertex buffers
glBindBuffer(GL_ARRAY_BUFFER,VBO);
// glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),&vertices[0],GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER,vertices,GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
// glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),&indices[0],GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,indices,GL_STATIC_DRAW);
// // set the vertex attribute pointers
// // vertex Positions
glEnabLevertexAttribArray(0);
// glVertexAttribPointer(0,GL_FLOAT,GL_FALSE,sizeof(vec3),(void*)0);
glVertexAttribPointer(0,3 * sizeof(float),(void*)0);
// glBindVertexArray(VAO);
// set attribute pointers for matrix (4 times vec4)
glEnabLevertexAttribArray(3);
glVertexAttribPointer(3,sizeof(glm::mat4),(void*)0);
glEnabLevertexAttribArray(4);
glVertexAttribPointer(4,(void*)(sizeof(glm::vec4)));
glEnabLevertexAttribArray(5);
glVertexAttribPointer(5,(void*)(sizeof(glm::vec4) * 2));
glEnabLevertexAttribArray(6);
glVertexAttribPointer(6,(void*)(sizeof(glm::vec4) * 3));
glVertexAttribDivisor(3,1);
glVertexAttribDivisor(4,1);
glVertexAttribDivisor(5,1);
glVertexAttribDivisor(6,1);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);
}
chunk.hpp
#pragma once
#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include "voxel.hpp"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using std::vector;
using glm::mat4;
using glm::vec3;
class Chunk {
vec3 centerPoint;
int voxelNum;
int shaderProgram;
unsigned int VBO,EBO,VAO;
mat4 VP;
public:
Chunk(vec3 center,float radius,int rinv);
void setVP(mat4 vp);
void setVisible(bool v);
void draw();
};
chunk.cpp
#include "chunk.hpp"
#include <iostream>
#include <vector>
#include <algorithm>
#include <stdlib.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using std::vector;
using glm::mat4;
using glm::vec3;
Chunk::Chunk(vec3 centerPoint,int rinv) {
vec3 endPoint(centerPoint.x - radius,centerPoint.z - radius);
float distVox = 2 * radius/rinv;
voxelNum = pow(rinv,3);
mat4* modelMatrices = new mat4[voxelNum];
srand(glfwGetTime()); // initialize random seed
for (int z = 0; z < rinv; z++) {
for (int y = 0; y < rinv; y++) {
for (int x = 0; x < rinv; x++) {
glm::mat4 model = glm::mat4(1.0f);
model = translate(model,vec3(endPoint.x + (x + 0.5) * distVox,endPoint.y + (y + 0.5) * distVox,endPoint.z + (z + 0.5) * distVox));
model = scale(model,vec3(radius));
int index = x + y * rinv + z * pow(rinv,2);
modelMatrices[index] = model;
}
}
}
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 3) in mat4 aInstanceMatrix;\n"
"uniform mat4 VP;\n"
"void main()\n"
"{\n"
" gl_Position = VP * aInstanceMatrix * vec4(aPos,1.0);\n"
"}\n\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec3 color;\n"
"void main()\n"
"{\n"
" FragColor = vec4(color,1.0f);\n"
"}\n\0";
// vertex shader
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader,&vertexShaderSource,NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader,GL_COMPILE_STATUS,&success);
if (!success)
{
glGetShaderInfoLog(vertexShader,512,NULL,infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_Failed\n" << infoLog << std::endl;
}
// fragment shader
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader,&fragmentShaderSource,NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader,&success);
if (!success)
{
glGetShaderInfoLog(fragmentShader,infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_Failed\n" << infoLog << std::endl;
}
// link shaders
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram,vertexShader);
glAttachShader(shaderProgram,fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram,GL_LINK_STATUS,&success);
if (!success) {
glGetProgramInfoLog(shaderProgram,infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_Failed\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glGenBuffers(1,&VBO);
glBindBuffer(GL_ARRAY_BUFFER,VBO);
glBufferData(GL_ARRAY_BUFFER,voxelNum * sizeof(mat4),&modelMatrices[0],GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);
}
void Chunk::setVP(mat4 vp) {
VP = vp;
}
void Chunk::draw() {
gluseProgram(shaderProgram);
gluniformMatrix4fv(glGetUniformlocation(shaderProgram,"VP"),&VP[0][0]);
Voxel eVox(vec3(0.0f),1.0f,true,false);
glBindVertexArray(eVox.VAO);
glDrawElementsInstanced(GL_TRIANGLES,36,GL_UNSIGNED_INT,voxelNum);
glBindVertexArray(0);
}
main.cpp
#include <iostream>
using namespace std;
#include "chunk.hpp"
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;
//Global Variables
GLFWwindow* window;
const char* SCR_TITLE = "WORKINGPLANET";
const int SCR_WIDTH = 500,SCR_HEIGHT = 500;
float x_rot = 0.0f;
float y_rot = 0.0f;
float y_rot_clamp = 89.999f;
// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;
void mouseCallback(GLFWwindow *window,int button,int action,int mods);
vec3 X_AXIS = vec3(1.0f,0.0f,0.0f);
vec3 Y_AXIS = vec3(0.0f,0.0f);
//Main Program
int main()
{
//Constructor Code
if(!glfwInit())
{
cerr << "Error!!GLFW";
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MInor,3);
glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT,GL_TRUE);
if(!(window = glfwCreateWindow(SCR_WIDTH,SCR_HEIGHT,SCR_TITLE,NULL)))
{
cerr << "Error!!GLFW window";
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
std::cout << "Failed to initialize OpenGL context" << std::endl;
return -1;
}
Chunk chunk(vec3(0.0f),0.5,2);
mat4 view = mat4(1.0);
vec3 cameraPos = glm::vec3(0.0f,4.0f);
view = lookAt(cameraPos,vec3(0,0),0));
//Loop Events
while(!glfwWindowShouldClose(window))
{
// per-frame time logic
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glClearColor(1.0,1.0,1.0);
glClear(GL_COLOR_BUFFER_BIT);
// Tweak these values to change the sensitivity
float scale_x = 7.0f / SCR_WIDTH;
float scale_y = 7.0f / SCR_HEIGHT;
float rotSpeed = 350.0f;
float rot = scale_x * rotSpeed;
if (glfwGetKey(window,GLFW_KEY_W) == GLFW_PRESS) {
rot = scale_y * rotSpeed;
if (y_rot + rot > y_rot_clamp)
rot = y_rot_clamp - y_rot;
view = rotate(view,(float)radians(rot),X_AXIS);
y_rot += rot;
} if (glfwGetKey(window,GLFW_KEY_S) == GLFW_PRESS) {
rot = scale_y * rotSpeed;
if (y_rot - rot < -y_rot_clamp)
rot = y_rot + y_rot_clamp;
view = rotate(view,(float)radians(-rot),X_AXIS);
y_rot -= rot;
} if (glfwGetKey(window,GLFW_KEY_A) == GLFW_PRESS) {
view = rotate(view,Y_AXIS);
x_rot -= rot;
} if (glfwGetKey(window,GLFW_KEY_D) == GLFW_PRESS) {
view = rotate(view,Y_AXIS);
x_rot += rot;
} if (glfwGetKey(window,GLFW_KEY_R) == GLFW_PRESS) {
view = lookAt(cameraPos,0));
x_rot = 0.0f;
y_rot = 0.0f;
}
mat4 projection = perspective(radians(45.0f),(float)SCR_WIDTH / (float)SCR_HEIGHT,0.1f,100.0f);
//Rendering
chunk.setVP(projection * view);
chunk.draw();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
有趣的是,注释掉 chunk.cpp 中的 glLinkProgram(shaderProgram);
使这个错误完全不同,块显示为一个包含整个立方体的巨大体素。
解决方法
您的 VBO 设置毫无意义。您将每个实例的转换矩阵设置为使用与 Voxel::generateElement()
中的几何图形相同的数据。
您稍后将所有变换矩阵上传到单独的 VBO,但属性指针仍指向几何 VBO。您需要将实例化属性的属性设置从 Voxel::generateElement()
移到 Chunk::Chunk()
中,以便您可以告诉它使用该 VBO 作为模型矩阵的源。