说明
在阅读此博客前,请访问2018级山东大学计算机学院图形学实验汇总。
原笔记通过latex编写,csdn只支持latex部分功能,所以下面主要是将pdf截屏上传。
Bresenham
课本上的讲解十分清晰,这里不再赘述,需要注意,使用避免浮点数的方法是将 0.5 替换为 dx。
需要注意的是,课本上的算法部分只能够画 x 正方向逆时针旋转 0-45 度的场景,其余情况需要将代码稍作修改。
修改的方式可以采用矩阵乘法实现旋转功能,但是我没有实现,希望大家可以尝试一下。我只采用了一种普通的方式。
代码
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "shader.h"
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
const unsigned int MAXN=600*800*2;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const char* vertexShaderPath="/Users/longzhengtian/Desktop/代码/C++代码/ComputerGraphics/shader.vs";
const char* fragmentShaderPath="/Users/longzhengtian/Desktop/代码/C++代码/ComputerGraphics/shader.fs";
unsigned int VBO, VAO, theSize;
int clickwho=-1;
bool DrawLine=false,DrawLineEnd=false;
struct Point{//像素点
int x,y;
}Line[2];
vector<float> DrawTheLine;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void Bresenham(int x0,int y0,int x1,int y1);//画线算法
void CoordinateTransformation(int x0,int y0,int x1,int y1);//坐标变换函数
void drawpixei(int x,int y,int y0);
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);}
bool distant(Point p1,Point p2){return (pow(fabs(p1.x-p2.x),2)+pow(fabs(p1.y-p2.y),2)<=25);}
int main(){
//实例化glfw函数
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
//创建glfw窗口
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Breseham", nullptr, nullptr);
if (window == nullptr){
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);//将window设置为接下来操作的主窗口
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){//使用glad加载glfw的所有函数指针
cout << "Failed to initialize GLAD" << endl;
return -1;
}
Shader ourShader(vertexShaderPath,fragmentShaderPath);//创建着色器程序
float verticesLine[MAXN];
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(verticesLine), verticesLine, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
while (!glfwWindowShouldClose(window)){
//输入
processInput(window);
//渲染命令
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//作图
ourShader.setVec4("ourColor",1.0f,0.0f,0.0f,1.0f);
ourShader.use();
glBindVertexArray(VAO);
glDrawArrays(GL_POINTS, 0, theSize/3);
//检查并调用事件,交换缓冲
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
void drawpixei(int x,int y){
DrawTheLine.push_back(transX(x));
DrawTheLine.push_back(transY(y));
DrawTheLine.push_back(1.0f);
}
void Bresenham(int x0,int y0,int x1,int y1){
int dx=x1-x0,dy=y1-y0;
int ux=(dx>0)?1:-1;//x增加的方向,是加还是减
int uy=(dy>0)?1:-1;//y增加的方向,是加还是减
int x=x0,y=y0;
dx=abs(dx); dy=abs(dy);
if(dx>dy){
int e=-dx;
for (x=x0; x!=x1; x+=ux){
drawpixei(x,y);
e+=2*dy;
if(e>=0){
y+=uy; e-=2*dx;
}
}
}
else {
int e=-dy;
for (y=y0; y!=y1; y+=uy){
drawpixei(x,y);
e+=2*dx;
if(e>=0){
x+=ux; e-=2*dy;
}
}
}
}
/*
void Bresenham(int x0,int y0,int x1,int y1){
int dx=x1-x0,dy=y1-y0,e=-dx,x=x0,y=y0;
for (int i=0; i<=dx; i++){
drawpixei(x,y);
x++; e+=2*dy;
if(e>=0) {
y++; e-=2*dx;
}
}
}
*/
void processInput(GLFWwindow *window){
if(clickwho!=-1){//处在移动状态
double xpos,ypos;
glfwGetCursorPos(window,&xpos,&ypos);
Line[clickwho].x=(int)xpos; Line[clickwho].y=(int)ypos;
DrawTheLine.clear();
Bresenham(Line[0].x,Line[0].y,Line[1].x,Line[1].y);
GLfloat* tempvec=DrawTheLine.data();
theSize=DrawTheLine.size();
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER,0,theSize*sizeof(GLfloat), tempvec);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height){
glViewport(0, 0, width, height);
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
else if (key == GLFW_KEY_L && action == GLFW_PRESS) {//如果按下"L",画直线
if(!DrawLine){//开始画直线
double xpos,ypos;
glfwGetCursorPos(window,&xpos,&ypos);
Line[0].x=(int)xpos; Line[0].y=(int)ypos;
clickwho=1; DrawLine=true;//此时状态和点击了第二个点是一样点,都是第二个点在移动
}
else {//结束画直线
clickwho=-1; DrawLine=false; DrawLineEnd=true;
}
}
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods){
if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS && !DrawLine){//此时没有画直线
double xpos,ypos;
glfwGetCursorPos(window,&xpos,&ypos);
if(clickwho!=-1) clickwho=-1;//移动中点击了鼠标,肯定是要结束了
else if(distant(Line[0],{(int)xpos,(int)ypos})) clickwho=0;//点击直线的第一个点
else if(distant(Line[1],{(int)xpos,(int)ypos})) clickwho=1;//点击直线的第二个点
}
}