OpenGL(可编程管线):Bezier曲面

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);
}

运行结果

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ht巷子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值