Bezier曲面
- Bezier线
- de Casteljau算法
- Bezier曲面
- bezier曲面编程思想
- 实例
- 代码
- 运行结果
Bezier线
贝塞尔曲线由一组参数方程定义,方程组中使用控制点指定特定的曲线的形状,每个控制点都是2D空间中的一个点。
de Casteljau算法
其中B(t)为Bernstein多项式,另外了解,这里不做解释(主要是没有深入研究…)。
此图来自《计算机图形学编程(使用OpenGL和C++)》
Bezier曲面
Bezier曲面定义了3D空间中的曲面,从Bezier曲线拓展,需要将bezier曲线的参数方程从一个参数拓展为两个参数。对于bezier曲线参数为t,在bezier曲面中参数为u和v。曲面的点由P(u, v)组成。
此图来自《计算机图形学编程(使用OpenGL和C++)》,此图为二次bezier曲面,有9个控制点
此时的参数方程为:
bezier曲面编程思想
我们将曲面横竖分别分成m,n份,那么曲面分成了m×n份,我们可以通过绘制m×n份近似矩形,就可以模拟出bezier曲面
实例
以三次bezier曲面为例:
假如我把bezier曲面分成了6×6块,如下图
在图中我们可以清晰的看出u,v坐标每次的增量是1/6,我们只要算出(u,v)坐标所在处的坐标就可以绘制出所需要的bezier曲面。
代码
构建bezier曲面类
#pragma once
#include <vector>
#include <glm/glm.hpp>
using namespace std;
class BezierFace
{
//顶点个数
int numVertices;
//索引个数
int numIndices;
//顶点
vector<glm::vec3> vertices;
//法线
vector<glm::vec3> normals;
//纹理
vector<glm::vec2> texCoords;
//索引
vector<int> indices;
//计算数据
void init(int prec);
//控制点
float* controlPoints;
vector<glm::vec3> controlPointsVector;
//曲线阶数
int step;
float toRadians(float degrees);
float Bernstein(float u, int index);
public:
BezierFace();
BezierFace(int step, float controlPoints[], int prec);
int getNumVertices();
int getNumIndices();
vector<glm::vec3> getVertices();
vector<glm::vec3> getNormals();
vector<glm::vec2> getTexCoords();
vector<int> getIndices();
};
#include "BezierFace.h"
void BezierFace::init(int prec)
{
numVertices = (prec + 1) * (prec + 1);
numIndices = prec * prec * 6;
for (int i = 0; i < numVertices; i++)
{
vertices.push_back(glm::vec3());
normals.push_back(glm::vec3());
texCoords.push_back(glm::vec2());
}
for (int i = 0; i < numIndices; i++)
{
indices.push_back(0);
}
for (int i = 0; i < (step + 1) * (step + 1) * 3; i += 3)
{
controlPointsVector.push_back(glm::vec3(controlPoints[i], controlPoints[i + 1], controlPoints[i + 2]));
}
for (int i = 0; i <= prec; i++)
{
for (int j = 0; j <= prec; j++)
{
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float u = (float)i / prec;
float v = (float)j / prec;
for (int i = 0; i <= 3; i++)
{
for (int j = 0; j <= 3; j++)
{
int index = i * 4 + j;
x += controlPointsVector[index].x * Bernstein(u, i) * Bernstein(v, j);
y += controlPointsVector[index].y * Bernstein(u, i) * Bernstein(v, j);
z += controlPointsVector[index].z * Bernstein(u, i) * Bernstein(v, j);
}
}
vertices[i * (prec + 1) + j] = glm::vec3(x, y, z);
normals[i * (prec + 1) + j] = glm::vec3(x, y, z);
texCoords[i * (prec + 1) + j] = glm::vec2((float)j / prec, (float)i / prec);
}
}
//计算索引
for (int i = 0; i < prec; i++) {
for (int j = 0; j < prec; j++) {
indices[6 * (i * prec + j) + 0] = i * (prec + 1) + j;
indices[6 * (i * prec + j) + 1] = i * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 2] = (i + 1) * (prec + 1) + j;
indices[6 * (i * prec + j) + 3] = i * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 4] = (i + 1) * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 5] = (i + 1) * (prec + 1) + j;
}
}
}
float BezierFace::toRadians(float degrees) { return (degrees * 2.0f * 3.14159f) / 360.0f; }
float BezierFace::Bernstein(float t, int index)
{
switch (index)
{
default:
case 0:
return pow(1.0 - t, 3);
break;
case 1:
return 3 * t * pow(1.0 - t, 2);
break;
case 2:
return 3 * pow(t, 2) * (1 - t);
break;
case 3:
return pow(t, 3);
break;
}
}
BezierFace::BezierFace(){}
BezierFace::BezierFace(int step, float controlPoints[], int prec)
{
this->step = step;
this->controlPoints = controlPoints;
init(prec);
}
int BezierFace::getNumVertices()
{
return numVertices;
}
int BezierFace::getNumIndices()
{
return numIndices;
}
vector<glm::vec3> BezierFace::getVertices()
{
return vertices;
}
vector<glm::vec3> BezierFace::getNormals()
{
return normals;
}
vector<glm::vec2> BezierFace::getTexCoords()
{
return texCoords;
}
vector<int> BezierFace::getIndices()
{
return indices;
}
运行实例:
#define GLEW_STATIC
#include <iostream>
#include "Camera.h"
#include "Circle.h"
#include "Shader.h"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm\gtc\type_ptr.hpp> // glm::value_ptr
#include <glm\gtc\matrix_transform.hpp>
#include <GLFW/glfw3.h>
#include <SOIL2/SOIL2.h>
#include "Ellipse.h"
#include "Ellipsoid.h"
#include "BezierFace.h"
#include "BezierLine.h"
using namespace std;
float controlPoints[] = {
-1.5, -1.5, 2.0, -0.5, -1.5, 2.0, 0.5, -1.5, -1.0, 1.5, -1.5, 2.0,
-1.5, -0.5, 1.0, -0.5, 1.5, 2.0, 0.5, 0.5, 1.0, 1.5, -0.5, -1.0,
-1.5, 0.5, 2.0, -0.5, 0.5, 1.0, 0.5, 0.5, 3.0, 1.5, -1.5, 1.5,
-1.5, 1.5, -2.0, -0.5, 1.5, -2.0, 0.5, 0.5, 1.0, 1.5, 1.5, -1.0
};
BezierFace myBezier = BezierFace(3, controlPoints, 100);
GLuint VAO, VBO[3];
GLuint texture;
Shader myShader;
Camera myCamera(glm::vec3(0.0f, 0.0f, 2.0f));
GLFWwindow* window;
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;
float lastX = 400.f;
float lastY = 300.f;
bool firstMouse = true;
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
{
myCamera.ProcessKeyboard(FORWARD, deltaTime);
}
else if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
{
myCamera.ProcessKeyboard(BACKWARD, deltaTime);
}
else if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
{
myCamera.ProcessKeyboard(LEFT, deltaTime);
}
else if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
{
myCamera.ProcessKeyboard(RIGHT, deltaTime);
}
}
void mouse_callback(GLFWwindow* window, double xPos, double yPos)
{
if (firstMouse)
{
lastX = xPos;
lastY = yPos;
firstMouse = false;
}
float deltaX, deltaY;
deltaX = xPos - lastX;
deltaY = lastY - yPos;
lastX = xPos;
lastY = yPos;
myCamera.ProcessMouseMovement(deltaX, deltaY);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
myCamera.ProcessMouseScroll(yoffset);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
GLuint loadTexture(const char* texImagePath)
{
GLuint textureRef;
textureRef = SOIL_load_OGL_texture(texImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
if (textureRef == 0) cout << "didnt find texture file " << texImagePath << endl;
// ----- mipmap/anisotropic section
glBindTexture(GL_TEXTURE_2D, textureRef);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) {
GLfloat anisoset = 0.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoset);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoset);
}
// ----- end of mipmap/anisotropic section
return textureRef;
}
void setupVertices()
{
vector<float> pvalues; //顶点坐标
vector<float> tvalues; //纹理坐标
vector<float> nvalues; //法线
vector<int> ind = myBezier.getIndices();
vector<glm::vec3> verts = myBezier.getVertices();
vector<glm::vec2> tex = myBezier.getTexCoords();
vector<glm::vec3> norm = myBezier.getNormals();
for (int i = 0; i < myBezier.getNumIndices(); i++)
{
pvalues.push_back(verts[ind[i]].x);
pvalues.push_back(verts[ind[i]].y);
pvalues.push_back(verts[ind[i]].z);
tvalues.push_back(tex[ind[i]].s);
tvalues.push_back(tex[ind[i]].t);
nvalues.push_back(norm[ind[i]].x);
nvalues.push_back(norm[ind[i]].y);
nvalues.push_back(norm[ind[i]].z);
}
glGenVertexArrays(1, &VAO);
glGenBuffers(3, VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferData(GL_ARRAY_BUFFER, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);
glBufferData(GL_ARRAY_BUFFER, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW);
}
void init(GLFWwindow* window)
{
myShader = Shader("bezierVert.vert", "bezierFrag.frag");
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
setupVertices();
texture = loadTexture("flower.png");
}
void display(GLFWwindow* window, double currentTime)
{
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//输入
processInput(window);
myShader.use();
glm::mat4 projection = glm::perspective(glm::radians(myCamera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f);
glm::mat4 view = myCamera.GetViewMatrix();
glm::mat4 model = glm::mat4(1.0f);
myShader.setMat4("projection", projection);
myShader.setMat4("view", view);
myShader.setMat4("model", model);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glDrawArrays(GL_TRIANGLES, 0, myBezier.getNumIndices());
}
int main() {
if(!glfwInit()) {
exit(EXIT_FAILURE);
}
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);
GLFWwindow* window = glfwCreateWindow(1600, 1200, "Ellipsoid", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
//glfwSwapInterval(1);
glViewport(0, 0, 1600, 1200);
glEnable(GL_DEPTH_TEST);
init(window);
while (!glfwWindowShouldClose(window)) {
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
着色器
#version 330
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
TexCoord = aTexCoord;
}
#version 330
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D s;
void main()
{
FragColor = texture(s, TexCoord);
}