OpenGL 缺陷 - 不需要的插值
OpenGL defect - undesirable interpolation
我正在尝试使用 Bèzier Surfaces 渲染犹他茶壶,并且在这方面取得了成功。然而,我有一些完全不受欢迎的缺陷。如您所见here,茶壶看起来不错,但存在一些不必要的'interpolation'。您认为这可能是什么原因造成的?顺便说一下,当我将对象写入 .ply 文件时,我可以设法使用 meshlab 打开它,没有问题也没有缺陷。最后,我使用 Xcode 作为我的开发环境。
非常感谢您提供的任何帮助。
编辑: 添加了部分代码。首先,控制点和使用的补丁是从this旧论文中检索的。
struct position3D { GLfloat x, y, z; };
struct position3D teapot_cp_vertices[] = {
// 1
{ 1.4 , 0.0 , 2.4 },
{ 1.4 , -0.784 , 2.4 },
{ 0.784 , -1.4 , 2.4 },
{ 0.0 , -1.4 , 2.4 },
.
.
.
}
#define TEAPOT_NB_PATCHES 32
#define ORDER 3
unsigned short teapot_patches[][ORDER + 1][ORDER + 1] =
{
// rim
{ { 1, 2, 3, 4 },{ 5, 6, 7, 8 },{ 9, 10, 11, 12 },{ 13, 14, 15, 16, } },
{ { 4, 17, 18, 19 },{ 8, 20, 21, 22 },{ 12, 23, 24, 25 },{ 16, 26, 27, 28, } },
{ { 19, 29, 30, 31 },{ 22, 32, 33, 34 },{ 25, 35, 36, 37 },{ 28, 38, 39, 40, } },
{ { 31, 41, 42, 1 },{ 34, 43, 44, 5 },{ 37, 45, 46, 9 },{ 40, 47, 48, 13, } },
...
...
...
} }
}
所有的点和补丁都可以在提到的论文中找到。
用于渲染茶壶的顶点和三角形使用以下方法计算:
int factorial(int n)
{
assert(n >= 0);
return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;
}
float binomial_coefficient(int i, int n) {
assert(i >= 0); assert(n >= 0);
return 1.0f * factorial(n) / (factorial(i) * factorial(n-i));
}
float bernstein_polynomial(int i, int n, float u) {
return binomial_coefficient(i, n) * powf(u, i) * powf(1-u, n-i);
}
void build_control_points_k(int p, struct position3D control_points_k[][ORDER+1]) {
for (int i = 0; i <= ORDER; i++) {
for (int j = 0; j <= ORDER; j++) {
control_points_k[i][j] = teapot_cp_vertices[teapot_patches[p][i][j] - 1];
}
}
}
Vertex compute_position(struct position3D control_points_k[][ORDER+1], float u, float v) {
Vertex result = *new Vertex();
for (int i = 0; i <= ORDER; i++) {
float poly_i = bernstein_polynomial(i, ORDER, u);
for (int j = 0; j <= ORDER; j++) {
float poly_j = bernstein_polynomial(j, ORDER, v);
result.x += poly_i * poly_j * control_points_k[i][j].x;
result.y += poly_i * poly_j * control_points_k[i][j].y;
result.z += poly_i * poly_j * control_points_k[i][j].z;
result.r = 0; //default colour
result.g = 0;
result.b = 0;
}
}
return result;
}
#define RESU 10 //resolution in u axis
#define RESV 10 //resolution in v axis
void Object3D::build_teapot() {
vlist = new Vertex[TEAPOT_NB_PATCHES * RESU*RESV]; //vertex list
tlist = new Triangle[TEAPOT_NB_PATCHES * (RESU-1)*(RESV-1) * 2]; //triangle list
//calculate vertices
for (int p = 0; p < TEAPOT_NB_PATCHES; p++) {
struct position3D control_points_k[ORDER+1][ORDER+1];
build_control_points_k(p, control_points_k);
for (int ru = 0; ru <= RESU-1; ru++) {
float u = 1.0 * ru / (RESU-1);
for (int rv = 0; rv <= RESV-1; rv++) {
float v = 1.0 * rv / (RESV-1);
vlist[p*RESU*RESV + ru*RESV + rv] = compute_position(control_points_k, u, v);
vlist[p*RESU*RESV + ru*RESV + rv].r = 1.0 * p / TEAPOT_NB_PATCHES;
vlist[p*RESU*RESV + ru*RESV + rv].g = 1.0 * p / TEAPOT_NB_PATCHES;
vlist[p*RESU*RESV + ru*RESV + rv].b = 0.7;
}
}
}
//calculate triangle vertex orders or namely triangles
int n = 0;
Triangle tmpTrg = *new Triangle();
tmpTrg.nverts = 3;
for (int p = 0; p < TEAPOT_NB_PATCHES; p++) {
for (int ru = 0; ru < RESU-1; ru++)
for (int rv = 0; rv < RESV-1; rv++) {
// ABCD is a square
// triangle in the order ABC is the first one
tmpTrg.verts = new int[tmpTrg.nverts];
tmpTrg.verts[0] = p*RESU*RESV + ru *RESV + rv ;
tmpTrg.verts[1] = p*RESU*RESV + ru *RESV + (rv+1);
tmpTrg.verts[2] = p*RESU*RESV + (ru+1)*RESV + (rv+1);
tlist[n] = tmpTrg;
n++;
// triangle in the order CDA is the second one
tmpTrg.verts = new int[tmpTrg.nverts];
tmpTrg.verts[0] = p*RESU*RESV + (ru+1)*RESV + (rv+1);
tmpTrg.verts[1] = p*RESU*RESV + (ru+1)*RESV + rv ;
tmpTrg.verts[2] = p*RESU*RESV + ru *RESV + rv ;
tlist[n] = tmpTrg;
n++;
}
}
}
这里是GL初始化函数:
void init(int w, int h)
{
// Init GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// macOSX requirement :
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
glfwWindowHint(GLFW_SAMPLES, 4);
window = glfwCreateWindow(w, h, "OpenGLTeapot", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
// Initialize GLEW to setup the OpenGL Function pointers
glewExperimental = GL_TRUE;
glewInit();
// Define the viewport dimensions
glViewport(0, 0, w, h);
// Enable depth test
glEnable(GL_DEPTH_TEST);
}
编辑 2 : 我尝试 运行 使用 Visual Studio15 的 Windows 计算机上的代码,但没有这样的缺陷。有没有人知道什么可能导致这样一个愚蠢的问题?
编辑 3: 这是对象创建代码:
void Object3D::CreateObject()
{
int attributeCount = 6;
vertexCount = TEAPOT_NB_PATCHES * RESU*RESV;
triangleCount = TEAPOT_NB_PATCHES * (RESU-1)*(RESV-1) * 2;
build_teapot();
//Bind the vertex and index buffers
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
// Bind our Vertex Array Object first, then bind and set our buffers and pointers.
glBindVertexArray(VAO);
//Convert our vertex list into a continuous array, copy the vertices into the vertex buffer.
float* vertexData = new float[vertexCount * attributeCount];
for (int i = 0; i < vertexCount; i++)
memcpy(&vertexData[i*attributeCount],
vlist[i].getAsArray(), sizeof(float)*attributeCount);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*attributeCount*vertexCount, vertexData, GL_STATIC_DRAW);
//Copy the index data found in the list of triangles into the element array buffer (index array)
//We are using a triangles, so we need triangleCount * 3 indices.
int* indexData = new int[triangleCount * 3];
for (int i = 0; i < triangleCount; i++)
memcpy(&indexData[i * 3], tlist[i].verts, sizeof(int) * 3);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*3*triangleCount, indexData, GL_STATIC_DRAW);
// Position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, attributeCount * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, attributeCount * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
// Unbind VAO
glBindVertexArray(0);
// Delete temporary buffers
delete[] vertexData;
delete[] indexData;
}
主要功能:
int main()
{
int screenWidth = 800;
int screenHeight = 600;
init(screenWidth, screenHeight);
std::vector<Object3D*> listOfObjects;
Object3D* pObj = new Object3D();
pObj->CreateObject();
listOfObjects.push_back(pObj);
//Create the shaders.
Shader shader(VertexShaderPath, FragmentShaderPath);
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
// Clear the colorbuffer
glClearColor(0.0f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use the shader
shader.Use();
// Transformations
// Create camera transformation
glm::mat4 view;
glm::vec3 cameraPos = glm::vec3(0.0f, 150.0f, 100.0f);
glm::vec3 cameraTarget = glm::vec3(0.0, 80.0f, 20.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
view = glm::lookAt(cameraPos, cameraTarget, cameraUp);
// Create projection transformation
glm::mat4 projection;
projection = glm::perspective<float>(90.0, (float)screenWidth / (float)screenHeight, 0.1f, 1000.0f);
// Get the uniform locations
GLint modelLoc = glGetUniformLocation(shader.Program, "model");
GLint viewLoc = glGetUniformLocation(shader.Program, "view");
GLint projLoc = glGetUniformLocation(shader.Program, "projection");
// Pass the view and projection matrices to the shaders
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Put the bottom of the object on XZ plane and scale it up
pObj->modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(30.0f));
pObj->modelMatrix = glm::rotate(pObj->modelMatrix, -90.0f, glm::vec3(1.0f, 0.0f,0.0f));
for (auto pObj : listOfObjects)
{
glBindVertexArray(pObj->VAO);
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(pObj->modelMatrix));
glDrawElements(GL_TRIANGLES, (pObj->vertexCount) * 6, GL_UNSIGNED_INT, 0);
//glDrawElements(GL_TRIANGLES, 50, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
// Swap the buffers
glfwSwapBuffers(window);
}
for (auto pObj : listOfObjects)
{
glDeleteVertexArrays(1, &pObj->VAO);
glDeleteBuffers(1, &pObj->VBO);
glDeleteBuffers(1, &pObj->EBO);
delete pObj;
}
glfwTerminate();
return 0;
}
问题似乎与 glDrawElements 函数的偏移量有关。当我将该行更改为:
glDrawElements(GL_TRIANGLES, (listOfObjects[i]->vertexCount) * 6, GL_UNSIGNED_INT, (void*)(sizeof(Vertex)));
它正确呈现。
我正在尝试使用 Bèzier Surfaces 渲染犹他茶壶,并且在这方面取得了成功。然而,我有一些完全不受欢迎的缺陷。如您所见here,茶壶看起来不错,但存在一些不必要的'interpolation'。您认为这可能是什么原因造成的?顺便说一下,当我将对象写入 .ply 文件时,我可以设法使用 meshlab 打开它,没有问题也没有缺陷。最后,我使用 Xcode 作为我的开发环境。
非常感谢您提供的任何帮助。
编辑: 添加了部分代码。首先,控制点和使用的补丁是从this旧论文中检索的。
struct position3D { GLfloat x, y, z; };
struct position3D teapot_cp_vertices[] = {
// 1
{ 1.4 , 0.0 , 2.4 },
{ 1.4 , -0.784 , 2.4 },
{ 0.784 , -1.4 , 2.4 },
{ 0.0 , -1.4 , 2.4 },
.
.
.
}
#define TEAPOT_NB_PATCHES 32
#define ORDER 3
unsigned short teapot_patches[][ORDER + 1][ORDER + 1] =
{
// rim
{ { 1, 2, 3, 4 },{ 5, 6, 7, 8 },{ 9, 10, 11, 12 },{ 13, 14, 15, 16, } },
{ { 4, 17, 18, 19 },{ 8, 20, 21, 22 },{ 12, 23, 24, 25 },{ 16, 26, 27, 28, } },
{ { 19, 29, 30, 31 },{ 22, 32, 33, 34 },{ 25, 35, 36, 37 },{ 28, 38, 39, 40, } },
{ { 31, 41, 42, 1 },{ 34, 43, 44, 5 },{ 37, 45, 46, 9 },{ 40, 47, 48, 13, } },
...
...
...
} }
}
所有的点和补丁都可以在提到的论文中找到。
用于渲染茶壶的顶点和三角形使用以下方法计算:
int factorial(int n)
{
assert(n >= 0);
return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;
}
float binomial_coefficient(int i, int n) {
assert(i >= 0); assert(n >= 0);
return 1.0f * factorial(n) / (factorial(i) * factorial(n-i));
}
float bernstein_polynomial(int i, int n, float u) {
return binomial_coefficient(i, n) * powf(u, i) * powf(1-u, n-i);
}
void build_control_points_k(int p, struct position3D control_points_k[][ORDER+1]) {
for (int i = 0; i <= ORDER; i++) {
for (int j = 0; j <= ORDER; j++) {
control_points_k[i][j] = teapot_cp_vertices[teapot_patches[p][i][j] - 1];
}
}
}
Vertex compute_position(struct position3D control_points_k[][ORDER+1], float u, float v) {
Vertex result = *new Vertex();
for (int i = 0; i <= ORDER; i++) {
float poly_i = bernstein_polynomial(i, ORDER, u);
for (int j = 0; j <= ORDER; j++) {
float poly_j = bernstein_polynomial(j, ORDER, v);
result.x += poly_i * poly_j * control_points_k[i][j].x;
result.y += poly_i * poly_j * control_points_k[i][j].y;
result.z += poly_i * poly_j * control_points_k[i][j].z;
result.r = 0; //default colour
result.g = 0;
result.b = 0;
}
}
return result;
}
#define RESU 10 //resolution in u axis
#define RESV 10 //resolution in v axis
void Object3D::build_teapot() {
vlist = new Vertex[TEAPOT_NB_PATCHES * RESU*RESV]; //vertex list
tlist = new Triangle[TEAPOT_NB_PATCHES * (RESU-1)*(RESV-1) * 2]; //triangle list
//calculate vertices
for (int p = 0; p < TEAPOT_NB_PATCHES; p++) {
struct position3D control_points_k[ORDER+1][ORDER+1];
build_control_points_k(p, control_points_k);
for (int ru = 0; ru <= RESU-1; ru++) {
float u = 1.0 * ru / (RESU-1);
for (int rv = 0; rv <= RESV-1; rv++) {
float v = 1.0 * rv / (RESV-1);
vlist[p*RESU*RESV + ru*RESV + rv] = compute_position(control_points_k, u, v);
vlist[p*RESU*RESV + ru*RESV + rv].r = 1.0 * p / TEAPOT_NB_PATCHES;
vlist[p*RESU*RESV + ru*RESV + rv].g = 1.0 * p / TEAPOT_NB_PATCHES;
vlist[p*RESU*RESV + ru*RESV + rv].b = 0.7;
}
}
}
//calculate triangle vertex orders or namely triangles
int n = 0;
Triangle tmpTrg = *new Triangle();
tmpTrg.nverts = 3;
for (int p = 0; p < TEAPOT_NB_PATCHES; p++) {
for (int ru = 0; ru < RESU-1; ru++)
for (int rv = 0; rv < RESV-1; rv++) {
// ABCD is a square
// triangle in the order ABC is the first one
tmpTrg.verts = new int[tmpTrg.nverts];
tmpTrg.verts[0] = p*RESU*RESV + ru *RESV + rv ;
tmpTrg.verts[1] = p*RESU*RESV + ru *RESV + (rv+1);
tmpTrg.verts[2] = p*RESU*RESV + (ru+1)*RESV + (rv+1);
tlist[n] = tmpTrg;
n++;
// triangle in the order CDA is the second one
tmpTrg.verts = new int[tmpTrg.nverts];
tmpTrg.verts[0] = p*RESU*RESV + (ru+1)*RESV + (rv+1);
tmpTrg.verts[1] = p*RESU*RESV + (ru+1)*RESV + rv ;
tmpTrg.verts[2] = p*RESU*RESV + ru *RESV + rv ;
tlist[n] = tmpTrg;
n++;
}
}
}
这里是GL初始化函数:
void init(int w, int h)
{
// Init GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// macOSX requirement :
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
glfwWindowHint(GLFW_SAMPLES, 4);
window = glfwCreateWindow(w, h, "OpenGLTeapot", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
// Initialize GLEW to setup the OpenGL Function pointers
glewExperimental = GL_TRUE;
glewInit();
// Define the viewport dimensions
glViewport(0, 0, w, h);
// Enable depth test
glEnable(GL_DEPTH_TEST);
}
编辑 2 : 我尝试 运行 使用 Visual Studio15 的 Windows 计算机上的代码,但没有这样的缺陷。有没有人知道什么可能导致这样一个愚蠢的问题?
编辑 3: 这是对象创建代码:
void Object3D::CreateObject()
{
int attributeCount = 6;
vertexCount = TEAPOT_NB_PATCHES * RESU*RESV;
triangleCount = TEAPOT_NB_PATCHES * (RESU-1)*(RESV-1) * 2;
build_teapot();
//Bind the vertex and index buffers
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
// Bind our Vertex Array Object first, then bind and set our buffers and pointers.
glBindVertexArray(VAO);
//Convert our vertex list into a continuous array, copy the vertices into the vertex buffer.
float* vertexData = new float[vertexCount * attributeCount];
for (int i = 0; i < vertexCount; i++)
memcpy(&vertexData[i*attributeCount],
vlist[i].getAsArray(), sizeof(float)*attributeCount);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*attributeCount*vertexCount, vertexData, GL_STATIC_DRAW);
//Copy the index data found in the list of triangles into the element array buffer (index array)
//We are using a triangles, so we need triangleCount * 3 indices.
int* indexData = new int[triangleCount * 3];
for (int i = 0; i < triangleCount; i++)
memcpy(&indexData[i * 3], tlist[i].verts, sizeof(int) * 3);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*3*triangleCount, indexData, GL_STATIC_DRAW);
// Position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, attributeCount * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, attributeCount * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
// Unbind VAO
glBindVertexArray(0);
// Delete temporary buffers
delete[] vertexData;
delete[] indexData;
}
主要功能:
int main()
{
int screenWidth = 800;
int screenHeight = 600;
init(screenWidth, screenHeight);
std::vector<Object3D*> listOfObjects;
Object3D* pObj = new Object3D();
pObj->CreateObject();
listOfObjects.push_back(pObj);
//Create the shaders.
Shader shader(VertexShaderPath, FragmentShaderPath);
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
// Clear the colorbuffer
glClearColor(0.0f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use the shader
shader.Use();
// Transformations
// Create camera transformation
glm::mat4 view;
glm::vec3 cameraPos = glm::vec3(0.0f, 150.0f, 100.0f);
glm::vec3 cameraTarget = glm::vec3(0.0, 80.0f, 20.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
view = glm::lookAt(cameraPos, cameraTarget, cameraUp);
// Create projection transformation
glm::mat4 projection;
projection = glm::perspective<float>(90.0, (float)screenWidth / (float)screenHeight, 0.1f, 1000.0f);
// Get the uniform locations
GLint modelLoc = glGetUniformLocation(shader.Program, "model");
GLint viewLoc = glGetUniformLocation(shader.Program, "view");
GLint projLoc = glGetUniformLocation(shader.Program, "projection");
// Pass the view and projection matrices to the shaders
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// Put the bottom of the object on XZ plane and scale it up
pObj->modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(30.0f));
pObj->modelMatrix = glm::rotate(pObj->modelMatrix, -90.0f, glm::vec3(1.0f, 0.0f,0.0f));
for (auto pObj : listOfObjects)
{
glBindVertexArray(pObj->VAO);
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(pObj->modelMatrix));
glDrawElements(GL_TRIANGLES, (pObj->vertexCount) * 6, GL_UNSIGNED_INT, 0);
//glDrawElements(GL_TRIANGLES, 50, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
// Swap the buffers
glfwSwapBuffers(window);
}
for (auto pObj : listOfObjects)
{
glDeleteVertexArrays(1, &pObj->VAO);
glDeleteBuffers(1, &pObj->VBO);
glDeleteBuffers(1, &pObj->EBO);
delete pObj;
}
glfwTerminate();
return 0;
}
问题似乎与 glDrawElements 函数的偏移量有关。当我将该行更改为:
glDrawElements(GL_TRIANGLES, (listOfObjects[i]->vertexCount) * 6, GL_UNSIGNED_INT, (void*)(sizeof(Vertex)));
它正确呈现。