OpenGL学习进程(5)第三课:视口与裁剪区域

本节是OpenGL学习的第三个课时,下面介绍如何运用显示窗体的视口和裁剪区域:

    (1)知识点引入:

    1)问题现象:

当在窗体中绘制图形后,拉伸窗体图形形状会发生变化:

#include <GL/glut.h>
#include <math.h>
const float Pi = 3.1415926f;
const int n = 1000;
const float R = 0.8f;

void init(void)
{
    glClearColor(0.0,0.0,0.0,0.0);//设背景色为黑色
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-1.0f,1.0f,-1.0f,1.0f);
}
void paintCircle()
{
    int i;
    glColor3f(1.0, 1.0, 1.0);//红色的圆
    glBegin(GL_POLYGON);
    for (i = 0; i<n; ++i)
        glVertex2f(R*cos(2 * Pi / n*i) , R*sin(2 * Pi / n*i));
    glEnd();
    glFlush();
}

int main(int argv,char **argc)
{
    glutInit(&argv,argc);
    glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);
    glutInitWindowSize(300,300);
    glutInitWindowPosition(800,300);
    init();
    glutCreateWindow("检测形状变化");
    glutDisplayFunc(paintCircle);
    glutMainLoop();
}

2)问题产生的原因:

没有正确设置投影矩阵。默认的是透视投影矩阵且高宽比为1。因此高宽比改变了,投影就会变形。因此只要高宽比改变了,投影就应该重新计算。

3)解决办法:

每当窗口的大小改变时,视口和裁剪区域必须重新定义,以适应新的窗口大小。只有这样,才能够使窗口中显示的图像保持原来的形状,而不发生扭曲:

#include <GL/glut.h>
#include <math.h>
const float Pi = 3.1415926f;
const int n = 1000;
const float R = 30.0f;

void RenderScene()
{
    glClear(GL_COLOR_BUFFER_BIT);
    // 把当前绘图颜色设置为红色
    glColor3f(1.0f, 0.0f, 0.0f);
    // OpenGL命令,用当前的绘图颜色绘制一个填充矩形
    glRectf(-50.0f, 50.0f, 50.0f, -50.0f);
    // 刷新绘图命令,此时所有未执行的OpenGL命令被执行
    glFlush();

    int i;
    glColor3f(0.0, 1.0, 0.0);//绿色的圆
    glBegin(GL_POLYGON);
    for (i = 0; i<n; ++i)
        glVertex2f(R*cos(2 * Pi / n*i), R*sin(2 * Pi / n*i));
    glEnd();
    glFlush();
}

// 设置渲染状态
void SetupRC()
{
    // 设置用于清除窗口的颜色
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
}

// 当窗口大小改变时由GLUT函数库调用
void ChangeSize(GLsizei w, GLsizei h)
{
    //
    GLfloat aspectRatio;
    // 防止被0所除
    if (0 == h){
        h = 1;
    }
    // 设置视口为窗口的大小
    glViewport(0, 0, w, h);
    // 选择投影矩阵,并重置坐标系统
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    // 计算窗口的纵横比(像素比)
    aspectRatio = (GLfloat)w / (GLfloat)h;
    // 定义裁剪区域(根据窗口的纵横比,并使用正投影)
    if (w <= h) {
        glOrtho(-100.0, 100.0, -100 / aspectRatio, 100 / aspectRatio, 1.0, -1.0);
    }
    else {
        glOrtho(-100.0 * aspectRatio, 100.0 *aspectRatio, -100.0, 100.0, 1.0, -1.0);
    }
    // 选择模型视图矩阵,并重置坐标系统
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowSize(300,300);
    glutInitWindowPosition(400,300);
    glutCreateWindow("SubstainSize");
    glutDisplayFunc(RenderScene);
    // 设置当窗口的大小发生变化时的回调函数
    glutReshapeFunc(ChangeSize);
    // 设置渲染状态
    SetupRC();
    // 启动GLUT框架的运行,一经调用便不再返回,直到程序终止
    glutMainLoop();

    return 0;
}

    (2)代码解释:

    1)void glutReshapeFunc( void(*func) (int width,int height) )

GLUT定义了当窗口大小改变时glutReshapeFunc()函数应该被调用。此外,这个函数还会在窗口初次被创建时调用,保证初始化窗口不是正方形的时候渲染也不会变形出错。

    2)自定义函数ChangeSize(GLsizei w, GLsizei h)

API中规定此函数要做的工作有:

1.计算高宽比(wight/height)。(注意为了计算正确,我们必须保证高度不为0。)

2.用函数glViewport把视口设置为整个窗口。

3.设置当前矩阵为投影矩阵,这个矩阵定义了一个可视空间(viewing volume),再调用一个单位矩阵来初始化投影矩阵。

4.根据窗口的纵横比定义裁剪区域,并使用正投影。

5.选择模型视图矩阵,并重置坐标系统。

另外此函数还可以用gluPerspective配合gluLookAt()来编写,同样能达到目的(http://blog.csdn.net/nauty_li/article/details/2227143)。

    3)SetupRC()设置渲染状态:

在渲染新的图形时,需要做一些准备工作。在本例中做的工作就是重设背景色,防止上一张的图像对即将绘制的产生影响。

    4)void glViewport( GLint x, GLint y, GLsizei width, GLsizei height);

x, y Specify the lower left corner of the viewport rectangle, in pixels. The initial value is (0,0).

width, height Specify the width and height of the viewport. When a GL context is first attached to a window, width and height are set to the dimensions of that window.

glViewport specifies the affine transformation of xx and yy from normalized device coordinates to window coordinates.

调用glViewPort函数来决定视见区域,告诉OpenGL应把渲染之后的图形绘制在窗体的哪个部位。当视见区域是整个窗体时,OpenGL将把渲染结果绘制到整个窗口。

glViewport()函数可以实现拆分窗口的功能。

    (3)相关知识:

    1)定义视口(窗口内部的渲染区域)

void glViewport(GLint x, GLint y, GLsizei width, GLsizeiheight);

其中,x,y参数指定了窗口内部视口的左下角位置,width和height参数指定了视口的大小(以屏幕像素为单位)。

    2)定义裁剪区域

对裁剪区域进行重新定义,使纵横比保持不变,窗口仍然维持在原来的形状。也就是根据新窗口大小的纵横比(像素之比,使用屏幕坐标系统),重新定义裁剪区域的纵横比(逻辑单位之比,使用笛卡尔坐标系统),使裁剪区域与视口的纵横比保持一致,这就是保持图像形状不变的关键所在。

我们在裁剪区域中使用了正投影:

void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);

注:纵横比指的是垂直方向上一个单位长度内的像素数量与水平方向上一个单位长度内的像素数量之比。

3)GL_PROJECTION和GL_MODELVIEW和GL_TEXTURE(矩阵变换http://blog.sina.com.cn/s/blog_537cc4d9010172o9.html)

glMatrixMode就是对接下来要做什么进行一下声明,这几个都是它的参数。

GL_PROJECTION:投影的意思,就是要对投影相关进行操作。也就是把物体投影到一个平面上,就像我们照相一样,把3维物体投到2维的平面上。这样,接下来的语句可以是跟透视相关的函数,比如glFrustum()或gluPerspective()。

GL_MODELVIEW:这个是对模型视景的操作,接下来的语句描绘一个以模型为基础的适应,这样来设置参数,接下来用到的就是像gluLookAt()这样的函数。

GL_TEXTURE:就是对纹理相关进行操作。

    (4)运用模型视景和裁剪区域的实例:

#include <GL/glut.h>

//不显示控制台窗口
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void paint()
{   //glViewport的四个参数,前两个代表模型视景的起点坐标,后两个代表视景的宽度和高度

    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 0.0);
    //画分割线,分成四个视见区
    glViewport(0, 0, 400, 400);
    //多组双顶点线段,点成对出现
    glBegin(GL_LINES);
    glVertex2f(-1.0, 0);
    glVertex2f(1.0, 0);
    glVertex2f(0.0, -1.0);
    glVertex2f(0.0, 1.0);
    glEnd();

    //定义在左下角
    glColor3f(0.0, 1.0, 0.0);
    glViewport(0, 0, 200, 200);
    glBegin(GL_POLYGON);
    glVertex2f(-0.5, -0.5);
    glVertex2f(-0.5, 0.5);
    glVertex2f(0.5, 0.5);
    glVertex2f(0.5, -0.5);
    glEnd();

    //定义在右上角
    glColor3f(0.0, 0.0, 1.0);
    glViewport(200, 200, 200, 200);
    glBegin(GL_POLYGON);
    glVertex2f(-0.5, -0.5);
    glVertex2f(-0.5, 0.5);
    glVertex2f(0.5, 0.5);
    glVertex2f(0.5, -0.5);
    glEnd();

    //定义在左上角
    glColor3f(1.0, 0.0, 0.0);
    glViewport(0, 200, 200, 200);
    glBegin(GL_POLYGON);
    glVertex2f(-0.5, -0.5);
    glVertex2f(-0.5, 0.5);
    glVertex2f(0.5, 0.5);
    glVertex2f(0.5, -0.5);
    glEnd();

    //定义在右下角
    glColor3f(1.0, 1.0, 0.0);
    glViewport(200, 0, 200, 200);
    glBegin(GL_POLYGON);
    glVertex2f(-0.5, -0.5);
    glVertex2f(-0.5, 0.5);
    glVertex2f(0.5, 0.5);
    glVertex2f(0.5, -0.5);
    glEnd();
    glFlush();
}

void init()
{
    glClear(GL_COLOR_BUFFER_BIT);
    //灰色作为填充背景
    glClearColor(0.5, 0.5, 0.5, 0.5);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //定义裁剪区域
    gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
}

int main(int argc, char ** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(400, 200);
    glutInitWindowSize(400, 400);
    glutCreateWindow("视口和裁剪区域应用");
    init();//和下面一行的顺序可以颠倒,还可以省略,省略后采用默认设置,背景为黑色
    glutDisplayFunc(paint);
    glutMainLoop();
}

时间: 2024-10-11 00:00:13

OpenGL学习进程(5)第三课:视口与裁剪区域的相关文章

OpenGl学习进程(8)第六课:点、边和图形(三)绘制图形

本节是OpenGL学习的第六个课时,下面介绍OpenGL图形的相关知识:     (1)多边形的概念: 多边形是由多条线段首尾相连而形成的闭合区域.OpenGL规定,一个多边形必须是一个“凸多边形”.通过点.直线和多边形,就可以组合成各种几何图形.一段弧可以看成是是很多短的直线段相连,这些直线段足够短,以至于其长度小于一个像素的宽度.通过位于不同平面的相连的小多边形,还可以组成一个“曲面”. 什么是凸边形: 凸边形:多边形内任意两点所确定的线段都在多边形内,由此也可以推导出,凸多边形不能是空心的

OpenGl学习进程(7)第五课:点、边和图形(二)边

本节是OpenGL学习的第五个课时,下面介绍OpenGL边的相关知识: (1)边的概念: 数学上的直线没有宽度,但OpenGL的直线则是有宽度的.同时,OpenGL的直线必须是有限长度,而不是像数学概念那样是无限的.可以认为,OpenGL的“直线”概念与数学上的“线段”接近,它可以由两个端点来确定.     (2)如何绘制边: 1)OpenGL支持绘制三种类型的边: GL_LINES :指定两个顶点,在它们之间绘制一条直线.如果为GL_LINES指定了奇数个顶点,那么最后一个顶点会被忽略. GL

OpenGL学习进程(11)第八课:颜色绘制的详解

    本节是OpenGL学习的第八个课时,下面将详细介绍OpenGL的颜色模式,颜色混合以及抗锯齿.     (1)颜色模式: OpenGL支持两种颜色模式:一种是RGBA,一种是颜色索引模式. RGBA模式与索引模式的区别: 计算机必须为每个像素保存一些数据,在RGBA模式中数据就代表了颜色:而颜色索引模式中数据代表了一个索引,要获取真正的颜色值还需要查索引表.数据的数量是由帧缓存中的位面决定的.一个位面为一个像素的一个位的数据.假如是8位面的颜色,每个像素就有8个颜色位,因此就有2的8次方

OpenGL学习进程(6)第四课:点、边和图形(一)点

本节是OpenGL学习的第四个课时,下面介绍OpenGL点的相关知识:     (1)点的概念:     数学上的点,只有位置,没有大小.但在计算机中,无论计算精度如何提高,始终不能表示一个无穷小的点.一般情况下,OpenGL中的点将被画成单个的像素,虽然它可能足够小,但并不会是无穷小.同一像素上,OpenGL可以绘制许多坐标只有稍微不同的点,但该像素的具体颜色将取决于OpenGL的实现. 点是OpenGL中绘制一切的基础.     (2)如何绘制点: 1)OpenGL为点的绘制提供了一系列函数

OpenGL学习进程(10)第七课:四边形绘制与动画基础

    本节是OpenGL学习的第七个课时,下面以四边形为例介绍绘制OpenGL动画的相关知识:     (1)绘制几种不同的四边形: 1)四边形(GL_QUADS) OpenGL的GL_QUADS图元用于绘制四边形,它根据每四个顶点绘制一个四边形. 注意:在使用四边形时必需记住四边形的四个角必须位于同一个平面中(不存在弯曲的四边形). 2)四边形带(GL_QUAD_STRIP) 该图元指定一个连接的四边形带.它们都保持相同方向的环绕. 3)通用多边形GL_POLYGON 我们可以用它绘制任意数

OpenGL学习进程(3)第一课:初始化窗体

    本节是OpenGL学习的第一个课时,下面介绍如何初始化一个窗体:     (1)显示一个有蓝色背景的窗体: #include <GL/glut.h> #include <stdlib.h> void display(void) { /* clear all pixels */ glClear (GL_COLOR_BUFFER_BIT); glFlush (); } int main(int argc, char** argv) { glutInit(&argc, a

OpenGL学习进程(4)第二课:绘制图形

本节是OpenGL学习的第二个课时,下面介绍如何用点和线来绘制图形:     (1)用点的坐标来绘制矩形: #include <GL/glut.h> void display(void) { // clear all pixels glClear(GL_COLOR_BUFFER_BIT); // draw yellow polygon (rectangle) with corners at glColor3f(1.0, 1.0, 0.0); glBegin(GL_POLYGON); //绘制开

OpenGL学习进程(13)第十课:基本图形的底层实现及算法原理

    本节介绍OpenGL中绘制直线.圆.椭圆,多边形的算法原理.     (1)绘制任意方向(任意斜率)的直线: 1)中点画线法: 中点画线法的算法原理不做介绍,但这里用到最基本的画0<=k<=1的中点画线法实现任意斜率k直线的绘制. 1)当A点x坐标值大于B点坐标值时,即A点在B点的右侧时,交换A.B点的坐标.保证A点在B的左侧. 2)考虑特殊情况,当直线AB的斜率不存在时,做近似处理,设置斜率为-(y0-y1)*100,即近似无穷大. 3)当斜率m满足0<=m<=1时,按书

OpenGL学习进程(2)OpenGL开发环境的搭建

    通过本节,我们来学习一下在Win10 64.VS1013环境下搭建OpenGL的开发环境.     (1)选择一个编译环境: 现在在windows中OpenGL的主流编译工具有Visual Studio,Broland C++ Builder,Dev C++等,我们选择Visual Studio2013作为开发环境.     (2)用到的资源: 我们使用了GLEW,freeglut以及GLTools这三个库.下面是它们的简介: 1)GLEW:   The OpenGL Extension