裁剪算法Liang-Barsky

该博客介绍了使用OpenGL和C++实现Liang-Barsky裁剪算法的交互系统。用户可以通过键盘操作在窗口中绘制、移动裁剪矩形,并实时观察裁剪效果。代码中详细展示了如何处理线段裁剪,以及坐标变换和鼠标点击事件的响应。

实验内容

• 实现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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值