实验内容
• 实现Liang-Barsky裁剪算法,绘制任意方向\数量线段,可移动的裁剪窗口,通过不同颜色标识裁剪窗口内外的部分,效果可参考下图(可交互的移动裁剪窗口并实时显示裁剪效果)。(100%)
• 画线的命令可以使用OpenGL提供的画线函数,也可以使用实验一自己实现的画线函数。

代码介绍
相关算法在课堂上已经讲过,这里只介绍相关代码。
在以下代码中按下R为画矩形模式,按下L为画直线模式,按下M为移动矩形模式。将所有已绘制的直线的顶点放入vector中存储,每次绘制图像时,遍历所有直线,调用Liang-Barsky算法进行裁剪,得到裁剪后的两顶点,放入另一vector中以绿色绘制,从而实现上图的效果。
要注意的是原有直线的绘制要在裁剪后直线的绘制之前,否则会覆盖掉裁剪的效果。
#include "shader.h"
using namespace std;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const unsigned int maxn = 800 * 600 * 3;
unsigned int VAO[4];
unsigned int VBO[4];
float cutWindow[maxn] = {};//裁剪窗口
float originLine[maxn] = {};//临时直线
float cutLine[maxn] = {};//裁剪窗口中的直线
float existLine[maxn] = {};//已有直线
//坐标变换为标准化设备坐标,即[-1.0,1.0]
float transX(int x) { return (float) ((float) (2 * x) - SCR_WIDTH) / (SCR_WIDTH * 1.0f); }
float transY(int y) { return (float) (SCR_HEIGHT - 2 * (float) y) / (SCR_HEIGHT * 1.0f); }
float transX(float x) { return (float) ((2.0 * x) - (float)SCR_WIDTH) / ((float)SCR_WIDTH * 1.0f); }
float transY(float y) { return (float) ((float)SCR_HEIGHT - 2.0 * y) / ((float)SCR_HEIGHT * 1.0f); }
bool click1, click2;
int drawAction;
struct Point {
int x, y;
} Line[2];
struct RectPoint {
int minX, maxX;
int minY, maxY;
} baseRect,nowRect;
vector<float> TheLine;//存储需要绘制的直线标准化顶点 x,y,z
vector<float> pixelLine;//存储直线顶点 x,y
vector<float> LineClip;
bool ClipT(float p, float q, float & u1, float & u2){
float r;
if(p < 0){//从外部指向内部
r = q / p;//求出u
if(r > u2) return false;
if(r > u1) u1 = r;//三个值中取较小值
}
else if(p > 0){//从内部指向外部
r = q / p;
if(r < u1) return false;
if(r < u2) u2 = r;//三个值中取较大值
}
else return q >= 0;//p == 0 与边界平行,判断是否在窗口内
return true;
}
void LiangBarskey(float x1, float y1, float x2, float y2, float XL, float XR, float YB, float YT){
float dx, dy;
float u1 = 0, u2 = 1;//原生两个端点的u值,分别对应左端点的和右端点
dx = x2 - x1;
dy = y2 - y1;//Pk
if(ClipT(-dx,x1-XL,u1,u2))
if(ClipT(dx,XR-x1,u1,u2))
if(ClipT(-dy,y1-YB,u1,u2))
if(ClipT(dy,YT-y1,u1,u2)){
LineClip.push_back(transX(x1 + u1 * dx));
LineClip.push_back(transY(y1 + u1 * dy));
LineClip.push_back(1.0f);
LineClip.push_back(transX(x1 + u2 * dx));
LineClip.push_back(transY(y1 + u2 * dy));
LineClip.push_back(1.0f);
}
}
void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
glViewport(0, 0, width, height);//0,0为左上角位置,后两参数为窗口高度和宽度
}
void processInput(GLFWwindow *window) {
if (click1) {
if (drawAction == 0) {//绘制矩形
int minX = min(Line[0].x, Line[1].x);
int maxX = max(Line[0].x, Line[1].x);
int minY = min(Line[0].y, Line[1].y);
int maxY = max(Line[0].y, Line[1].y);
baseRect.minX = minX;
baseRect.maxX = maxX;
baseRect.minY = minY;
baseRect.maxY = maxY;//进行存储便于后续移动
nowRect = baseRect;
float tempVertex[] = {
transX(minX), transY(minY), 1.0f,//左下
transX(minX), transY(maxY), 1.0f,//左上
transX(maxX), transY(minY), 1.0f,//右下
transX(maxX), transY(maxY), 1.0f//右上
};
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);//绑定缓冲
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(tempVertex), &tempVertex);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
if (drawAction == 1) {//移动矩形
int diffX = Line[1].x - Line[0].x;
int diffY = Line[1].y - Line[0].y;//变化量
nowRect.minX = baseRect.minX + diffX;
nowRect.maxX = baseRect.maxX + diffX;
nowRect.minY = baseRect.minY + diffY;
nowRect.maxY = baseRect.maxY + diffY;
float tempVertex[] = {
transX(nowRect.minX), transY(nowRect.minY), 1.0f,//左下
transX(nowRect.minX), transY(nowRect.maxY), 1.0f,//左上
transX(nowRect.maxX), transY(nowRect.minY), 1.0f,//右下
transX(nowRect.maxX), transY(nowRect.maxY), 1.0f//右上
};
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);//绑定缓冲
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(tempVertex), &tempVertex);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
if (drawAction == 2) {//绘制直线
float tempVertex[] = {
transX(Line[0].x), transY(Line[0].y), 1.0f,
transX(Line[1].x), transY(Line[1].y), 1.0f
};
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);//绑定缓冲
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(tempVertex), &tempVertex);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
//处理直线的裁剪问题
LineClip.clear();
for(int i = 0; i < pixelLine.size(); i += 4){
LiangBarskey(pixelLine[i],pixelLine[i+1],pixelLine[i+2],pixelLine[i+3],
(float)nowRect.minX,(float)nowRect.maxX,(float)nowRect.minY,(float)nowRect.maxY);
}
GLfloat *tempVec = LineClip.data();
glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);//绑定缓冲
glBufferSubData(GL_ARRAY_BUFFER, 0, (long long)(LineClip.size()*sizeof(GLfloat)), tempVec);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
//设定按键相应
void key_CallBack(GLFWwindow *window, int key, int scancode, int action, int mod) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {//ESC退出
glfwSetWindowShouldClose(window, true);
return;
}
if (key == GLFW_KEY_R && action == GLFW_PRESS) {//R画矩形
//矩形模式
drawAction = 0;
}
if (key == GLFW_KEY_M && action == GLFW_PRESS) {//M移动矩形
//移动模式
drawAction = 1;
}
if (key == GLFW_KEY_L && action == GLFW_PRESS) {//L画直线
//直线模式
drawAction = 2;
}
}
//设定鼠标点击响应
void mouse_click_callBack(GLFWwindow *window, int button, int action, int mod) {
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {//按下鼠标
//第一个点
double xPos, yPos;
glfwGetCursorPos(window, &xPos, &yPos);
Line[0].x = static_cast<int>(xPos);
Line[0].y = static_cast<int>(yPos);
//初始化
Line[1].x = static_cast<int>(xPos);
Line[1].y = static_cast<int>(yPos);
click1 = true;
click2 = false;
}
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {//松开鼠标
//第二个点
double xPos, yPos;
glfwGetCursorPos(window, &xPos, &yPos);
Line[1].x = static_cast<int>(xPos);
Line[1].y = static_cast<int>(yPos);
click2 = true;
click1 = false;
//处理矩形移动的存储问题
if (drawAction == 1) {
int diffX = Line[1].x - Line[0].x;
int diffY = Line[1].y - Line[0].y;//变化量
baseRect.minX += diffX;
baseRect.maxX += diffX;
baseRect.minY += diffY;
baseRect.maxY += diffY;
nowRect = baseRect;
}
//处理多条线段的存储问题
if(drawAction == 2){
//begin point
TheLine.push_back(transX(Line[0].x));
TheLine.push_back(transY(Line[0].y));
TheLine.push_back(1.0f);
pixelLine.push_back((float)Line[0].x);
pixelLine.push_back((float)Line[0].y);
//end point
TheLine.push_back(transX(Line[1].x));
TheLine.push_back(transY(Line[1].y));
TheLine.push_back(1.0f);
pixelLine.push_back((float)Line[1].x);
pixelLine.push_back((float)Line[1].y);
GLfloat *tempVec = TheLine.data();
glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);//绑定缓冲
glBufferSubData(GL_ARRAY_BUFFER, 0, (long long)(TheLine.size()*sizeof(GLfloat)), tempVec);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
}
void cursePosCallBack(GLFWwindow *window, double x, double y) {
if (!click2) {
Line[1].x = static_cast<int>(x);
Line[1].y = static_cast<int>(y);
}
}
int main() {
//初始化并配置
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//窗口创建
GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Liang-Barsky", nullptr, nullptr);
if (window == nullptr) {
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetKeyCallback(window, key_CallBack);//设定按键功能
glfwSetMouseButtonCallback(window, mouse_click_callBack);//设置鼠标点击
glfwSetCursorPosCallback(window, cursePosCallBack);
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {//使用glad加载函数指针
cout << "Filed initialize GLAD" << endl;
}
//着色器类,前者为顶点着色器,后者为片段着色器
Shader ourShader("D:/c++/CG_lab2/shader.vs", "D:/c++/CG_lab2/shader.fs");
//顶点缓冲对象
glGenVertexArrays(4, VAO);
glGenBuffers(4, VBO);
//窗口VAO VBO
glBindVertexArray(VAO[0]);
//将创建的缓冲绑定
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
//将数据复制到缓冲内存
glBufferData(GL_ARRAY_BUFFER, sizeof(cutWindow), cutWindow, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr);
glEnableVertexAttribArray(0);
//临时直线VAO VBO
glBindVertexArray(VAO[1]);
//将创建的缓冲绑定
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
//将数据复制到缓冲内存
glBufferData(GL_ARRAY_BUFFER, sizeof(originLine), originLine, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr);
glEnableVertexAttribArray(0);
//裁剪直线VAO VBO
glBindVertexArray(VAO[2]);
//将创建的缓冲绑定
glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);
//将数据复制到缓冲内存
glBufferData(GL_ARRAY_BUFFER, sizeof(cutLine), cutLine, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr);
glEnableVertexAttribArray(0);//VAO
//已有直线VAO VBO
glBindVertexArray(VAO[3]);
//将创建的缓冲绑定
glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);
//将数据复制到缓冲内存
glBufferData(GL_ARRAY_BUFFER, sizeof(existLine), existLine, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);//VBO
glBindVertexArray(0);
while (!glfwWindowShouldClose(window)) {
//输入
processInput(window);
//清空屏幕
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//裁剪窗口
ourShader.use();
ourShader.setVec4("ourColor", 0.2f, 0.4f, 0.4f, 1.0f);
glBindVertexArray(VAO[0]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//原始线条
ourShader.setVec4("ourColor", 1.0f, 0.5f, 0.2f, 1.0f);
ourShader.use();
glBindVertexArray(VAO[1]);
glDrawArrays(GL_LINES, 0, 2);
ourShader.setVec4("ourColor", 1.0f, 0.5f, 0.2f, 1.0f);
ourShader.use();
glBindVertexArray(VAO[3]);
glDrawArrays(GL_LINES, 0, (int)(TheLine.size()/3));
//裁剪后的线条
ourShader.setVec4("ourColor", 0.0f, 1.0f, 0.0f, 1.0f);
ourShader.use();
glBindVertexArray(VAO[2]);
glDrawArrays(GL_LINES, 0, (int)(LineClip.size()/3));
//检查并调用事件,交换缓冲
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(3, VAO);
glDeleteBuffers(3, VBO);
glfwTerminate();
return 0;
}
该博客介绍了使用OpenGL和C++实现Liang-Barsky裁剪算法的交互系统。用户可以通过键盘操作在窗口中绘制、移动裁剪矩形,并实时观察裁剪效果。代码中详细展示了如何处理线段裁剪,以及坐标变换和鼠标点击事件的响应。
5598

被折叠的 条评论
为什么被折叠?



