计算机图形:二维观察

二维观察流水线

  • 基本概念

裁剪窗口(clipping window):二维场景中要显示的部分。区域外场景都要裁去,只有裁剪窗口内的场景才能在屏幕上显示。裁剪窗口有时暗指世界窗口(world window)或观察窗口(viewing window)。裁剪窗口变小,可以观察到场景细节。

视口(viewport):用来控制在显示窗口的定位。对象在裁剪窗口内的部分映射到显示窗口中指定位置的视口中。多个视口,可在不同位置观察场景的不同部分,改变视口的尺寸可改变显示对象的尺寸和位置。

裁剪窗口与视口不同点:窗口选择要看什么内容,视口指定输出设备在什么位置观察
相同点:一般都是正则矩形(边与坐标轴平行),也可以用多边形,圆等形状。

二维观察变换(two-dimensional viewing transformation):场景描述从二维世界坐标系到设备坐标系的映射。也称窗口到视口的变换(window-to-viewport transformtion)或窗口变换(windowing transformation)。

二维观察变换步骤:

tips:规范化设备坐标系指设备坐标范围0 to 1或者-1 to 1。


裁剪窗口

图形软件仅支持正则矩形(边平行x/y轴)的矩形裁剪窗口,要实现特殊裁剪效果,如椭圆/样条曲线等形状,需要自己实现裁剪和坐标变换算法。

观察坐标系裁剪窗口

通常,在世界坐标系(world coordinate)中指定一个观察坐标系(view coordinate),再选定裁剪窗口:

建立观察坐标系方法:

  1. 世界坐标系下,位置\(P_0(x_0, y_0)\)作为观察坐标系原点,向量V作为\(y_{view}\)轴方向,则V称为二维观察向上向量(view up vector)。

  2. 在世界坐标系中旋转x、y轴一定角度,从而获得观察向上向量;然后将原点平移至目标位置。

\(u=(u_x, u_y), v=(v_x, v_y)\)分别为观察坐标系$ x_{view}, y_{view}$轴方向向量,对象坐标转换(世界坐标=>观察坐标)矩阵:

\[\tag{1} M_{WC,VC}=R\cdot T \]

T是将观察原点由\(P_0\)到世界原点的平移变换,R是平移变换后的观察坐标系与世界坐标系重合的旋转变换。

注意:点的坐标变换与坐标系的变换方向相反,参见计算机图形:三维坐标系变换

规范化和视口变换

视口坐标:0~1,即位于单位正方形内。裁剪后,单位正方形映射到输出显示设备。视口边界在与显示窗口对应的屏幕坐标系指定。

有些图形系统,将规范化早窗口-视口转换之前,有些合并成一步。

裁剪窗口->规范化视口

一般过程:
1)定义一个视口,坐标0~1;
2)按点的变换方式,将对象描述变换到该视口;

窗口内的点(xw, yw) -> 视口的点(xv, yv):

视口中的对象与窗口有相同的相对位置,满足:

\[\tag{2} \begin{aligned} {xv-xv_{min}\over xv_{max}-xv_{min}} &= {xw-xw_{min}\over xw_{max}-xw_{min}} \\ {yv-yv_{min}}\over yv_{max}-yv_{min} &= {yw-yw_{min}\over yw_{max}-yw_{min}} \end{aligned} \]

可得关于(xv, yv)的方程:

\[\tag{3} \begin{aligned} xv &= s_xxw+t_x \\ yv &= s_yyw+t_y \end{aligned} \]

其中,\(s_x, s_y\)称为缩放系数,\(t_x, t_y\)称为平移参数。

\[\tag{4} \begin{aligned} s_x &= {xv_{max}-xv_{min}\over xw_{max}-xw_{min}} \\ s_y &= {yv_{max}-yv_{min}\over yw_{max}-yw_{min}} \\ t_x &= {xw_{max}xv_{min}-xw_{min}xv_{max}\over xw_{max}-xw_{min}} \\ t_y &= {yw_{max}yv_{min}-yw_{min}yv_{max}\over yw_{max}-yw_{min}} \end{aligned} \]

式(3)是通过相对位置不变,来求出裁剪窗口到视口的变换。也可以通过平移、缩放操作,得到变换矩阵:

  1. 以点\((xw_{min}, yw_{min})\)为中心执行缩放变换,将窗口变换成视口的大小;
  2. \((xw_{min}, yw_{min})\)平移到\((xv_{min}, yv_{min})\)

复合转换矩阵:

\[\tag{5} \begin{aligned} M_{wc, vc} &= T\cdot S \\ &= \begin{bmatrix} 1 & 0 & xv_{min}-xw_{min} \\ 0 & 1 & yv_{min}-yw_{min} \\ 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} s_x & 0 & xw_{min}(1-s_x) \\ 0 & s_y & yw_{min}(1-s_y) \\ 0 & 0 & 1 \end{bmatrix} &= \begin{bmatrix} s_x & 0 & t_x \\ 0 & s_y & t_y \\ 0 & 0 & 1 \end{bmatrix} \end{aligned} \]

其中,\(s_x, s_y, t_x, t_y\)含义同(4)。

注意:只有裁剪窗口、视口具有相同纵横比,才能保持对象的相对比例不变。

规范化正方形是中心在原点、长宽都为2(x/y坐标都是-1~1)的正方形。

OpenGL二维观察函数

OpenGL只提供三维观察函数,不过可用于二维观察,且核心库包含一个视口函数。
GLU库提供指定二维裁剪窗口的函数,GLUT库提供处理显示窗口的函数。

OpenGL投影模式

选择裁剪窗口和视口前,必须先选择投影模式,将裁剪窗口到参数作为投影变换到一部分来设置。

glMatrixMode(GL_PROJECTION);

该函数将指定投影矩阵作为当前矩阵,它原来设定为单位矩阵。
下面函数将初始化当前矩阵为单位阵:

glLoadIdentity();

GLU裁剪窗口函数

定义一个二维裁剪窗口:

// xwmin, xwmax, ywmin, ywmax是二维世界坐标系下裁剪窗口边界
gluOrtho2D(xwmin, xwmax, ywmin, ywmax);

将场景映射到屏幕的正交投影。对于二维平面,只是将对象位置转换到规范化坐标系。
如果没有指定裁剪窗口,就用默认坐标\((xw_{min}, yw_{min})=(-1.0,-1.0), (xw_{max},yw_{max})=(1.0,1.0)\)

OpenGL视口函数

指定视口参数:

// xvmin, yvmin 指定视口左下角位置, 与显示窗口左下角对应
// vpwidth vpHeight 视口的宽度像素数和高度像素数
glViewport(xvmin, yvmin, vpWidth, vpHeight);

如果没有设置视口,则默认视口位置、大小与显示窗口一样(注意不是裁剪窗口)。

GLUT显示窗口

  • 创建GLUT显示窗口

GLUT库建立和管理显示窗口,不依赖与任意特定计算机。

// 初始化GLUT库
glutInit(&argc, argv);

// 设置窗口属性
glutInitWindowPosition(xTopLeft, yTopLeft); // 窗口位置
glutInitWindowSize(dwWidth, dwHeight);    // 窗口大小
glutCreateWindow("Title of Display Window"); // 创建窗口并指定标题

如果没有设置窗口属性,则默认尺寸300x300,默认位置(-1, -1)(交给窗口管理系统)。

  • GLUT显示窗口模式和颜色

选择颜色模式(RGB或索引号和不同的缓存组合,参数用逻辑或方式组合。

glutInitDisplayMode(mode);

默认模式:单缓存和RGB(或RGBA, GLUT_RGB等价于GLUT_RGBA)颜色模式。<=>

glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);

设置显示窗口颜色:
1)在RGB模式下

glClearColor(red, green, blue, alpha);

2)在颜色索引模式下

// index 颜色表中对应颜色值的索引
glClearIndex(index);
  • GLUT显示窗口标识(标题)

GLUT会从1开始,为每个窗口设定一个显示窗口标识(display-window Identifier)。
窗口标识windowID 标识窗口,后续可用于显示参数和删除显示窗口。

// 返回windowID, 保存了显示窗口标识
int windowID = glutCreateWindow("A display window");
  • 删除GLUT显示窗口
glutDestroyWindow(windowID);
  • 当前GLUT显示窗口

指定的任何一个显示窗口操作,都针对当前显示窗口,即最后创建的当前显示窗口(current display window),或者用下面命令指定:

glutSetWindow(windowID);

查询当前的显示窗口:

currentWindowID = glutGetWindow();

没有显示窗口或当前显示窗口已被删除时,返回0。

  • 修改GLUT显示窗口位置、大小

设置当前显示窗口的位置

// xNewTopLeft, yNewTopLeft 显示窗口左上角相当于屏幕左上角的新位置
glutPositionWindow(xNewTopLeft, yNewTopLeft);

设置当前显示窗口的尺寸

glutReshapeWindow(dwNewWidth, dwNewHeight);

扩展窗口到整个屏幕

glutFullScreen();

改变窗口尺寸/位置后,可能改变窗口纵横比,可用下面命令调整显示窗口到变化:

glutReshapeFunc(winReshapeFcn);
  • 管理多个GLUT显示窗口

将当前显示窗口变成一个图符,通过小图片或符号形式表示窗口

// 图符使用窗口的名字标记
glutIconifyWindow();

改变图符名字

glutSetIconTitle("Icon Name");

改变显示窗口名字

glutSetWindowTitle("New Window Name");

多窗口下,可指定某个显示窗口为当前窗口,调用“pop-window”使其在其他窗口之前,“push-window”。使其在其他窗口之后。

将窗口置于顶层

glutSetWindow(windowID);
glutPopWindow();

将窗口置于底层

glutSetWindow(windowID);
glutPushWindow();

隐藏窗口

glutHideWindow();

显示窗口

glutShowWindow();
  • GLUT子窗口

可以在一个选中的显示窗口中建立任意数量的二级显示窗口,称为子窗口。用于将显示窗口分成不同的显示区域。
创建子窗口:

// windowID 标识父窗口
// BottomLeft, yBottomLeft指定子窗口位置; width, height指定子窗口大小
glutCreateSubWindow(windowID, xBottomLeft, yBottomLeft, width, height);
  • 显示窗口屏幕光标形状

设置当前窗口屏幕光标形状。可选择的光标形状:按选定方向指向的箭头、双向箭头、旋转箭头、十字游丝、手表、问号,头盖骨和交叉腿骨图案。可以形状依赖于OS。
e.g. GLUT_CURSOR_UP_DOWN 上下箭头;GLUT_CURSOR_CYCLE旋转箭头;GLUT_CURSOR_WAIT手表图案;GLUT_CURSOR_DESTROY头盖骨和交叉腿骨图案。

glutSetCursor(shape);
  • GLUT显示窗口中观察图形对象

创建显示窗口并选定其位置、大小、颜色和其他特征后,需要指定窗口中显示什么。

// pictureDescrip 作为回调函数,指定当前窗口显示什么
glutDisplayFunc(pictureDescrip);

如果创建了多个显示窗口,则需要为每个显示窗口或子窗口重复这一过程。

如果显示窗口在重新显示过程中被破坏,且pop-window命令后调用了glutDisplayFunc,则需要用下面函数指出当前显示窗口到内容应更新。例如,显示窗口弹出式菜单。

glutPostRedisplay();

执行app(main loop)

程序装载完毕,创建、初始化显示窗口后,需要进入GLUT处理循环(GLUT processing loop),监听鼠标、键盘等交互事件。

glutMainLoop();

背景函数(idle function)

没有其他事件需要系统处理时,可以设置一个背景函数或更新动画参数:

glutIdleFunc(function);

查询系统参数

查询系统某些参数的当前值。e.g. GLUT_WINDOW_X 获取当前显示窗口左上角相当于屏幕左上角的x坐标位置;GLUT_WINDOW_WIDTH或GLUT_SCREEN_WIDTH获取当前显示窗口宽度或屏幕宽度。

glutGet(stateParam);

裁剪算法

裁剪算法(cliiping algorithm):用来消除指定区域内或区域外的图形部分的过程,称为裁剪算法,简称裁剪。通常用正则矩形进行裁剪,边界\(xw_{min}, xw_{max}, yw_{min}, yw_{max}\)

任何裁剪窗口外内容,都从将要送到输出设备上显示的场景中消除。

根据不同图元类型,二维裁剪算法主要包括:

  • 点裁剪
  • 线段裁剪(直线段)
  • 区域的裁剪(多边形)
  • 曲线的裁剪
  • 文字的裁剪

点裁剪

假设裁剪窗口是一个在标准位置的矩形(边平行于x、y轴),如果点P(x, y)满足:

\[\tag{6} \begin{aligned} xw_{min} &<= x <= xw_{max} \\ yw_{min} &<= y <= yw_{max} \end{aligned} \]

线段裁剪

线段和标准矩形裁剪区域关系:
1)线段位于矩形内;
2)线段与矩形相交;
3)线段位于矩形外。

线段裁剪以后的裁剪,涉及的裁剪算法较为复杂,后续在专门文章中描述。

posted @ 2023-09-12 14:52  明明1109  阅读(135)  评论(0编辑  收藏  举报