使用求值器让若干点均匀分布,形成曲线
#include <iostream>
#include <Windows.h>
// GLEW
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
GLuint WIDTH = 400, HEIGHT = 400;
GLfloat gX = 0, gY = 0;
int choosedPoint = -1;
GLfloat pointWidth = 9.0f;
#define POINTS_CNT 6
#define VALUE_PER_POINT 3
static GLfloat controlPoints[POINTS_CNT][VALUE_PER_POINT] = {}, originPoints[POINTS_CNT][VALUE_PER_POINT] = {
{-0.8f, 0.8f, 0.0f},
{-0.8f, 0.0f, 0.0f},
{0.0f, -0.0f, 0.0f},
{0.0f, -0.0f, 0.0f},
{0.8f, -0.0f, 0.0f},
{0.8f, -0.8f, 0.0f}
};
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouseButton_callback(GLFWwindow* window, int button, int action, int mode);
void mouseMove_callback(GLFWwindow* window, double xPos, double yPos);
void windowResize_callback(GLFWwindow* window, int width, int height);
void resetControlPoints()
{
for (int i = 0; i < POINTS_CNT; ++i)
{
for (int j = 0; j < VALUE_PER_POINT; ++j)
{
controlPoints[i][j] = originPoints[i][j];
}
}
}
//opengl在glfw中显示规则:
//1.画布中间坐标为0,0。长宽个1.0f个单位,超过1.0f的部分不会被绘制出来
void DrawLine()
{
glColor4b(99, 38, 13, 90);
glBegin(GL_LINES);
glVertex2f(-1 + gX * 4, 1 + gY * 4);//指定第一个点
glVertex2f(-0.8 + gX * 4, gY * 4 + 0.8);//指定第二个点
glEnd();//结束
}
void DrawBezier()
{
glColor3b(99, 38, 13);
//第三个参数最好是1.0f,
glMap1f(GL_MAP1_VERTEX_3, 0.0f, 0.8f, VALUE_PER_POINT, POINTS_CNT, &controlPoints[0][0]);
glEnable(GL_MAP1_VERTEX_3);
glBegin(GL_LINE_STRIP);
//glEvalCoord1f,从u1取值到u2,线性均匀排列,取多少次就是分多少段,如果段数少,可能会有明显的曲折,最好是0-1.0f
for (GLfloat i = 0; i <= 0.8; i += 0.02)
{
glColor3f(i, 1 - i, 0.4f + i);
glEvalCoord1f(i);
}
glEnd();
glColor3b(0, 0, 0);
glPointSize(pointWidth);
glBegin(GL_POINTS);
for (int i = 0; i < POINTS_CNT; ++i)
{
glVertex3fv(controlPoints[i]);
}
glEnd();
}
void render(GLFWwindow* window)
{
glClearColor(1.0f, 0.8f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
DrawLine();
DrawBezier();
}
void initWindow(GLFWwindow* window)
{
glfwMakeContextCurrent(window);
//设置按键回调
glfwSetKeyCallback(window, key_callback);
//设置鼠标按键点下松开的回调
glfwSetMouseButtonCallback(window, mouseButton_callback);
//设置鼠标移动的回调
glfwSetCursorPosCallback(window, mouseMove_callback);
//设置窗口大小改变的回调,让绘画区域在窗口中间
glfwSetWindowSizeCallback(window, windowResize_callback);
}
void initParam()
{
//显示规则:窗口左下角坐标为0,0;所以下行代码表示在窗口左下角向右向上的400个像素单位作为画布
glViewport(0, 0, WIDTH, HEIGHT);//设置显示区域400*400,但是可以拖动改变窗口大小
glLineWidth(3.0);//设置线条宽度,3个像素
glEnable(GL_BLEND);
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);//设置点圆滑
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);//设置线光滑
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
int main()
{
glfwInit();
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL", nullptr, nullptr);
if (window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
initWindow(window);
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
initParam();
resetControlPoints();
// Game loop
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
render(window);
glfwSwapBuffers(window);
}
// Terminate GLFW, clearing any resources allocated by GLFW.
glfwTerminate();
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)//按下esc退出程序
{
glfwSetWindowShouldClose(window, GL_TRUE);
}
else if (key == GLFW_KEY_LEFT)
{
gX -= 0.01;
}
else if (key == GLFW_KEY_RIGHT)
{
gX += 0.01;
}
else if (key == GLFW_KEY_UP)
{
gY += 0.01;
}
else if (key == GLFW_KEY_DOWN)
{
gY -= 0.01;
}
}
//窗口坐标转视图坐标
void windowPos2CoordiatePos(GLFWwindow* window, double xPos, double yPos, double *pX, double *pY)
{
int width, height;
glfwGetWindowSize(window, &width, &height);
xPos -= (width - WIDTH) / 2;
*pX = xPos / (WIDTH / 2) - 1;
yPos -= (height - HEIGHT) / 2;
*pY = 1 - yPos / (HEIGHT / 2);
}
//x,y视图坐标系坐标转xpos、ypos窗口坐标
void coordiatePos2WindowPos(GLFWwindow* window, double x, double y, double *pXPos, double *pYPos)
{
int width, height;
glfwGetWindowSize(window, &width, &height);
*pXPos = (x + 1) * (WIDTH / 2);
*pYPos = (1 - y) * (WIDTH / 2);
*pXPos += (width - WIDTH) / 2;
*pYPos += (height - HEIGHT) / 2;
}
//判断一个窗口坐标是否在视图坐标系的某个点中,
bool posInPoint(GLFWwindow* window, double xPos, double yPos, double x, double y, double pointWidth)
{
double trueXPos, trueYPos;
coordiatePos2WindowPos(window, x, y, &trueXPos, &trueYPos);
if (xPos >= trueXPos - pointWidth
&& xPos <= trueXPos + pointWidth
&& yPos >= trueYPos - pointWidth
&& yPos <= trueYPos + pointWidth)
{
return true;
}
return false;
}
void mouseButton_callback(GLFWwindow* window, int button, int action, int mode)
{
double xPos = 0, yPos = 0;
double x = 0, y = 0;
glfwGetCursorPos(window, &xPos, &yPos);
WCHAR str[200];
swprintf(str, 400, L"button %d, action %d, mode %x [%lf,%lf]\r\n", button, action, mode, xPos, yPos);
//OutputDebugString(str);
choosedPoint = -1;
windowPos2CoordiatePos(window, xPos, yPos, &x, &y);
for (int i = 0; i < POINTS_CNT && (GLFW_PRESS == action); ++i)
{
if (posInPoint(window, xPos, yPos, controlPoints[i][0], controlPoints[i][1], pointWidth))
{
choosedPoint = i;
break;
}
}
if (GLFW_PRESS == action && (GLFW_MOD_CONTROL & mode))
{
resetControlPoints();
}
}
void mouseMove_callback(GLFWwindow* window, double xPos, double yPos)
{
if (choosedPoint >= 0 && choosedPoint <= POINTS_CNT - 1)
{
double newX = 0, newY = 0;
windowPos2CoordiatePos(window, xPos, yPos, &newX, &newY);
controlPoints[choosedPoint][0] = newX;
controlPoints[choosedPoint][1] = newY;
}
}
void windowResize_callback(GLFWwindow* window, int width, int height)
{
WIDTH = HEIGHT = width > height ? height : width;
//左下角是0, 0坐标, x,y说就是相对于左下角的像素距离
glViewport((width - WIDTH) / 2, (height - HEIGHT) / 2, WIDTH, HEIGHT);
}
效果:
监控鼠标按下,如果在6个点里面,就允许拖动,
一共6个点,可以设置成3个、4个、5个,
监控键盘上下左右键,是直线线条上下左右移动