计算机图形学の二维几何变换(openGL)

二维图形变换:

基础:

矩阵计算啥的就不提了

这里看一下齐次坐标:

用n+1维向量表示n维向量, 主要解决透视空间的问题, 在OpenGL中, 使用的都是齐次坐标:

具体的可以参考这片博客:
https://blog.csdn.net/zhuiqiuzhuoyue583/article/details/95228010

二维图形变换:

基础的三种图形变换:

  • 平移
  • 缩放
  • 旋转

其余的变换通常都能转化为这三种最基础的变换

感觉二维变换中无法使用glut提供的三个3D矩阵变换函数来进行变换, 最终还是得手算矩阵…

反正最后大作业肯定用的3D, 这里的2D还是先放着吧

二维图形变换样例: 时钟

这个是实验上机时做的, 时间不太够, 所以完全是百度的仿制版

其中也没用到矩阵计算啥的, 感觉不太好, 之后重做一份

显示效果:

image-20201016153727479

源码:

#define NDEBUG
#ifndef GLUT_DISABLE_ATEXIT_HACK
#define GLUT_DISABLE_ATEXIT_HACK
#endif
#include <windows.h>
#include <gl/glut.h>
#include <math.h>
#include <stdio.h>
#include <vector>

using namespace std;

const int windowWidge=600, windowHeight=600;


void setPixel(GLint xCoord, GLint yCoord){
	glBegin (GL_POINTS);
	glVertex2i (xCoord, yCoord);
	glEnd();
}


//---------------------------------------------
// Bresenham 画圆
class screenPt {
private:
	GLint x, y;

public:
	screenPt() { x = y = 0; }
	void setCoords(GLint xCoordValue, GLint yCoordValue) {
		x = xCoordValue;
		y = yCoordValue;
	}
	GLint getx() const { return x; }
	GLint gety() const { return y; }
	void incremetx() { x++; }
	void decrementy() { y--; }
};


void circleMidp(GLint xc, GLint yc, GLint radius) {
	screenPt circPt;
	GLint p = 1 - radius;
	circPt.setCoords(0, radius);
	void circlePlotPoints(GLint, GLint, screenPt);
	circlePlotPoints(xc, yc, circPt);
	while (circPt.getx() < circPt.gety()) {
		circPt.incremetx();
		if (p < 0)
			p += 2 * circPt.getx() + 1;
		else {
			circPt.decrementy();
			p += 2 * (circPt.getx() - circPt.gety()) + 1;
		}
		circlePlotPoints(xc, yc, circPt);
	}
}
void circlePlotPoints(GLint xc, GLint yc, screenPt circPt) {
	setPixel(xc + circPt.getx(), yc + circPt.gety());
	setPixel(xc - circPt.getx(), yc + circPt.gety());
	setPixel(xc + circPt.getx(), yc - circPt.gety());
	setPixel(xc - circPt.getx(), yc - circPt.gety());
	setPixel(xc + circPt.gety(), yc + circPt.getx());
	setPixel(xc - circPt.gety(), yc + circPt.getx());
	setPixel(xc + circPt.gety(), yc - circPt.getx());
	setPixel(xc - circPt.gety(), yc - circPt.getx());
}

//---------------------------------------------
//组合模型变换: 时钟

const GLfloat PI = 3.141592653f;

// 显示的时间,时 分 秒
float h = 0.0f;
float m = 0.0f;
float s = 0.0f;



//定时器回调函数:
void timerFunc(int value){
	//设置时分秒针
	s += 1;
	int carry1 = 0;
	if (s >= 60){
		s = 0;
		carry1 = 1;
	}
	m += carry1;
	int carry2 = 0;
	if (m >= 60){
		m = 0;
		carry2 = 1;
	}
	h += carry2;
	if (h >= 24){
		h = 0;
	}

	//  重绘
	glutPostRedisplay();

	glutTimerFunc(1000, timerFunc, 1); // 每1000ms中执行一次

	return ;
}

//绘制函数
void clockDisplay(){

	// 用当前清除色清除颜色缓冲区,即设定窗口的背景色
	glClear(GL_COLOR_BUFFER_BIT);

	// 黑色
	glColor3f(0.1f, 0.2f, 0.1f);

	// 绘制时钟外边框
	int cx=windowWidge/2, cy=windowHeight/2;
	int R=150;
	circleMidp (cx, cy, R);		//这里用的 Bresenham 画圆
	circleMidp (cx, cy, R-3);

	//绘制时钟刻度:
	int lineNum=60;
	int w1=5, w2=2;
	float len1=10, len2=5;
	for(int i=0;i<lineNum;++i){
		//12条粗线
		if(i%5==0){
			glLineWidth(w1);
			//glColor3f(0.0, 1.0, 0.0);
			glBegin(GL_LINES);
			glVertex2f(cx + (R - len1)*sin(2 * PI / lineNum * i), cy + (R - len1)*cos(2 * PI / lineNum * i));
			glVertex2f(cx + R*sin(2 * PI / lineNum * i), cy + R*cos(2 * PI / lineNum * i));
			glEnd();
		}
		//其余细线
		else{
			glLineWidth(w2);  //其他的刻度
			//glColor3f(0.0, 0.0, 1.0);
			glBegin(GL_LINES);
			glVertex2f(cx + (R - len2)*sin(2 * PI / lineNum * i), cy + (R - len2)*cos(2 * PI / lineNum * i));
			glVertex2f(cx + R*sin(2 * PI / lineNum * i), cy + R*cos(2 * PI / lineNum * i));
			glEnd();
		}
	}

	//绘制时钟中心点
	int dotWidth=3;
	for(int i=1;i<=dotWidth;++i){
		circleMidp (cx, cy, i);
	}

	//绘制时分秒针
	int hLen=R-len1-50;
	int mLen=R-len1-25;
	int sLen=R-len1-5;

	//12小时制
	int h12= h >= 12 ? (h - 12) : h;
	float sAngle=s/60.0;
	float mAngle=(m * 60 + s) / 3600.0;
	float hAngle=(h12 * 60 * 60 + m * 60 + s) / (12 * 60 * 60);

	// 秒
	glLineWidth(1);
	glBegin(GL_LINES);
	glVertex2f(cx, cy);
	glVertex2f(cx + sLen*sin(2 * PI*sAngle), cy + sLen*cos(2 * PI*sAngle));
	glEnd();

	// 分
	glLineWidth(3);
	glBegin(GL_LINES);
	glVertex2f(cx, cy);
	glVertex2f(cx + mLen*sin(2 * PI*mAngle), cy + mLen*cos(2 * PI*mAngle));
	glEnd();

	// 时
	glLineWidth(5);
	glBegin(GL_LINES);
	glVertex2f(cx, cy);
	glVertex2f(cx + hLen*sin(2 * PI*hAngle), cy + hLen*cos(2 * PI*hAngle));
	glEnd();

	glFlush ();
	return ;
}

//---------------------------------------------

//初始化绘制
void init(){
	glClearColor(1.0,1.0,1.0,0.0);//清除颜色设置
	glMatrixMode(GL_PROJECTION);//设置投影方式

	//获取本地时间
	SYSTEMTIME sys;
	GetLocalTime(&sys);
	h = sys.wHour;
	m = sys.wMinute;
	s = sys.wSecond;

	gluOrtho2D (0.0,windowWidge*1.0,0.0,windowHeight*1.0);
	return ;
}

int main(int argc, char** argv){
	glutInit(&argc, argv);//初始化glut
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式为单缓冲,RGB模式
	glutInitWindowPosition(0,0);//设置窗口位置
	glutInitWindowSize(windowWidge,windowHeight);//设置窗口大小
	glutCreateWindow("时钟");//设置窗口标题
	init();
	glutDisplayFunc(clockDisplay);
	glutTimerFunc(1000, timerFunc, 1);
	glutMainLoop();
	return 0;
}

源码分析:

几个新知识点:

  1. 定时器:

    注册一个函数, 间隔一定时间再执行, 只执行一次, 所以源码中在调用函数中又添加了glutTimerFunc()

    glutTimerFunc(unsigned int msecs,void(*func)(int value),int value)
    
    • msecs
      在调用func函数之前要等待的时间(单位:毫秒)

    • func
      注册一个调用函数,在等待msecs毫秒之后执行

      注意func的返回值为void, 参数为int

    • value
      作为参数传递给func

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页