opengl基础学习专题 (三) 多边形绘制的几种样式

题外话


聪明人之所以不会成功,是由于他们缺乏坚韧的毅力。

——艾萨克·牛顿(1643年1月4日—1727年3月31日)英国


    

  也许可以理解为 想更深一步的时候,坚持,努力和聪明缺一不可. 挺直腰杆在此向您致敬,愿您仍在天国

仍潇洒的思索着,奔跑着.

正文

  在第二课中,我们学习了如何绘制几何图形,但大家如果多写几个程序,就会发现其实还是有些郁闷之处。

例如:点太小,难以看清楚;直线也太细,不舒服;或者想画虚线,但不知道方法只能用许多短直线,甚至用点组合而成。

这些问题将在本课中被解决。

下面就点、直线、多边形分别讨论。

1、关于点

点的大小默认为1个像素,但也可以改变之。改变的命令为glPointSize,其函数原型如下:

void glPointSize (GLfloat size);

size必须大于0.0f,默认值为1.0f,单位为“像素”。

(注意:对于具体的OpenGL实现,点的大小都有个限度的,如果设置的size超过最大值,则设置可能会有问题。)

这里顺带说一下 关于上面函数编译器行为,以Window 代码为例解释如下

//具体的函数原型声明
WINGDIAPI void APIENTRY glPointSize (GLfloat size);

/*
 *APIENTRY 是设置编译器行为的宏 本质是 __stdcall
 *
 *__stdcall是函数调用约定的一种,函数调用约定主要约束了两件事:
 *1.参数传递顺序
 *2.调用堆栈由谁(调用函数或被调用函数)清理
 *3.函数名(在编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
 *
 *C中函数缺省默认是 __cdecl
 *每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。
 *函数采用从右到左的压栈方式。
 *注意:对于可变参数的成员函数,始终使用__cdecl的转换方式。
 */
#define WINAPI      __stdcall
#define WINAPIV     __cdecl
#define APIENTRY    WINAPI

实际使用 glPointSize例子如下:

#include <glut.h>

/*
 *
 *这里是绘制的主函数
 */
static void __display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    glPointSize(5.0f);

    glBegin(GL_POINTS);

    glVertex2f(0.0f,0.0f);
    glVertex2f(0.5f,0.5f);

    glEnd();

    glFlush();
}

2、关于直线

(1)直线可以指定宽度:

void glLineWidth(GLfloat width);

其用法跟glPointSize类似。

(2)画虚线。

首先,使用glEnable(GL_LINE_STIPPLE); 来启动虚线模式(使用glDisable(GL_LINE_STIPPLE)可以关闭之)。

然后,使用glLineStipple来设置虚线的样式。

void glLineStipple(GLint factor, GLushort pattern);

pattern是由1和0组成的长度为16的序列,从最低位开始看,如果为1,则直线上接下来应该画的factor个点将被画为实的;如果为0,则直线上接下来应该画的factor个点将被画为虚的。

再细说一点

glEnable(GL_LINE_STIPPLE);
glLineStipple(1, Ox3F07);
//此时模式为Ox3F07(二进制形式为0011111100000111).
//它所画出来的直线是这样的:
//先连续绘制3个像素,然后连续5个像素留空,再连续绘制6个像素,最后两个像素留空(注意,首先是从低位开始的)。
//如果factor是2,那么这个模式便被扩展为:
//先连续绘制6个像素,然后连续10个像素留空,再连续绘制12个像素,最后4个像素留空。
//如果没有启用点画线功能,OpenGL会自动把pattern当做为OxFFFF,把factor当成1。

更详细的例子代码如下:

#include <glut.h>

//绘制函数
static void __display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_LINE_STIPPLE); //开启画虚线模式
    glLineStipple(2,0x0F0F); //虚线的样式
    glLineWidth(10.f);

    //画一个线
    glBegin(GL_LINES);
    glVertex2f(0.0f,0.0f);
    glVertex2f(0.5f,0.5f);
    glEnd();

    glDisable(GL_LINE_STIPPLE); //关闭画虚线模式
    glFlush();
}

3、关于多边形

多边形的内容较多,我们将讲述以下四个方面。

(1)多边形的两面以及绘制方式。

虽然我们目前还没有真正的使用三维坐标来画图,但是建立一些三维的概念还是必要的。

从三维的角度来看,一个多边形具有两个面。每一个面都可以设置不同的绘制方式:填充、只绘制边缘轮廓线、只绘制顶点,其中“填充”是默认的方式。

可以为两个面分别设置不同的方式。

glPolygonMode(GL_FRONT, GL_FILL);            // 设置正面为填充方式

glPolygonMode(GL_BACK, GL_LINE);             // 设置反面为边缘绘制方式

glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 设置两面均为顶点绘制方式

(2)反转

一般约定为“顶点以逆时针顺序出现在屏幕上的面”为“正面”,另一个面即成为“反面”。

生活中常见的物体表面,通常都可以用这样的“正面”和“反面”,“合理的”被表现出来

(请找一个比较透明的矿泉水瓶子,在正对你的一面沿逆时针画一个圆,并标明画的方向,然后将背面转为正面,画一个类似的圆,体会一下“正面”和“反面”。

你会发现正对你的方向,瓶的外侧是正面,而背对你的方向,瓶的内侧才是正面。正对你的内侧和背对你的外侧则是反面。

这样一来,同样属于“瓶的外侧”这个表面,但某些地方算是正面,某些地方却算是反面了)。

但也有一些表面比较特殊。例如“麦比乌斯带”(请自己Bing一下),可以全部使用“正面”或全部使用“背面”来表示。

可以通过glFrontFace函数来交换“正面”和“反面”的概念。

glFrontFace(GL_CCW);   // 设置CCW方向为“正面”,CCW即CounterClockWise,逆时针

glFrontFace(GL_CW);    // 设置CW方向为“正面”,CW即ClockWise,顺时针

下面是一个示例程序,请用它替换前面教程中的__display函数,并将glFrontFace(GL_CCW)修改为glFrontFace(GL_CW),并观察结果的变化。

#include <glut.h>

//绘制函数
static void __display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    glFrontFace(GL_CCW); // 设置逆时针方向为正面
    glPolygonMode(GL_FRONT, GL_FILL); // 设置正面为填充模式
    glPolygonMode(GL_BACK, GL_LINE); // 设置反面为线形模式

    //先逆时针绘制一个 正方形,在左下方
    glBegin(GL_POLYGON);
    glVertex2f(-0.5,-0.5f);
    glVertex2f(0.0f,-0.5f);
    glVertex2f(0.0f,0.0f);
    glVertex2f(-0.5f,0.0f);
    glEnd();

    //后顺时针绘制一个正方形,在右上方
    glBegin(GL_POLYGON);
    glVertex2f(0.0f,0.0f);
    glVertex2f(0.0f,0.5f);
    glVertex2f(0.5f,0.5f);
    glVertex2f(0.5f,0.0f);
    glEnd();

    glFlush();
}

(3)剔除多边形表面

在三维空间中,一个多边形虽然有两个面,但我们无法看见背面的那些多边形,而一些多边形虽然是正面的,但被其他多边形所遮挡。如果将无法看见的多边形和可见的多边形同等对待,无疑会降低我们处理图形的效率。在这种时候,可以将不必要的面剔除。

首先,使用glEnable(GL_CULL_FACE);来启动剔除功能(使用glDisable(GL_CULL_FACE)可以关闭之)

然后,使用glCullFace来进行剔除。

glCullFace的参数可以是GL_FRONT,GL_BACK或者GL_FRONT_AND_BACK,分别表示剔除正面、剔除反面、剔除正反两面的多边形。

注意:剔除功能只影响多边形,而对点和直线无影响。例如,使用glCullFace(GL_FRONT_AND_BACK)后,所有的多边形都将被剔除,所以看见的就只有点和直线。

(4)镂空多边形

直线可以被画成虚线,而多边形则可以进行镂空。

首先,使用glEnable(GL_POLYGON_STIPPLE);来启动镂空模式(使用glDisable(GL_POLYGON_STIPPLE)可以关闭之)。

然后,使用glPolygonStipple来设置镂空的样式。

void glPolygonStipple(const GLubyte *mask);

其中的参数mask指向一个长度为128字节的空间,它表示了一个32*32的矩形应该如何镂空。其中:第一个字节表示了最左下方的从左到右(也可以是从右到左,这个可以修改)8个像素是否镂空(1表示不镂空,显示该像素;0表示镂空,显示其后面的颜色),最后一个字节表示了最右上方的8个像素是否镂空。

对于mask定义 一种简单方式 是这样的

win + R => mspaint + Enter => 设置画布像素为 32*32 => 自己随便编辑 => 保存为 my.bmp =>程序读取这个文件 转成 mask

下面分享一个Opengl 中 一个 老例子 如下:

#include <sc_head.h>
#include <glut.h>

static GLubyte __masks[] = {
    0x00, 0x00, 0x00, 0x00,    //   这是最下面的一行
    0x00, 0x00, 0x00, 0x00,
    0x03, 0x80, 0x01, 0xC0,    //   麻
    0x06, 0xC0, 0x03, 0x60,    //   烦
    0x04, 0x60, 0x06, 0x20,    //   的
    0x04, 0x30, 0x0C, 0x20,    //   初
    0x04, 0x18, 0x18, 0x20,    //   始
    0x04, 0x0C, 0x30, 0x20,    //   化
    0x04, 0x06, 0x60, 0x20,    //   ,
    0x44, 0x03, 0xC0, 0x22,    //   不
    0x44, 0x01, 0x80, 0x22,    //   建
    0x44, 0x01, 0x80, 0x22,    //   议
    0x44, 0x01, 0x80, 0x22,    //   使
    0x44, 0x01, 0x80, 0x22,    //   用
    0x44, 0x01, 0x80, 0x22,
    0x44, 0x01, 0x80, 0x22,
    0x66, 0x01, 0x80, 0x66,
    0x33, 0x01, 0x80, 0xCC,
    0x19, 0x81, 0x81, 0x98,
    0x0C, 0xC1, 0x83, 0x30,
    0x07, 0xE1, 0x87, 0xE0,
    0x03, 0x3F, 0xFC, 0xC0,
    0x03, 0x31, 0x8C, 0xC0,
    0x03, 0x3F, 0xFC, 0xC0,
    0x06, 0x64, 0x26, 0x60,
    0x0C, 0xCC, 0x33, 0x30,
    0x18, 0xCC, 0x33, 0x18,
    0x10, 0xC4, 0x23, 0x08,
    0x10, 0x63, 0xC6, 0x08,
    0x10, 0x30, 0x0C, 0x08,
    0x10, 0x18, 0x18, 0x08,
    0x10, 0x00, 0x00, 0x08    // 这是最上面的一行
};

//bmp 图片创建函数
void bmp_create(GLubyte mask[], int len);

// 屏幕绘制函数
void display(void);

#define _STR_BMP "fly.bmp"

int main(int argc, char *argv[])
{
    //这里先 创建 一个bmp
    bmp_create(__masks, sizeof __masks);

    glutInit(&argc,argv); // 初始化glut环境
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); //设置窗口绘制为RGB模式和单缓冲模式
    glutInitWindowPosition(500,500);
    glutInitWindowSize(300,300);

    glutCreateWindow(argv[0]); //初始化启动窗口
    glutDisplayFunc(display);

    glutMainLoop();//循环等待函数,直到glut创建的窗口关闭

    return 0;
}

//bmp 图片创建函数
void
bmp_create(GLubyte mask[], int len)
{
    FILE *bmp = NULL;

    if (NULL == mask || len <= 0)
        ERR_EXIT("check params error [NULL == mask || len <= 0].");

    if ((bmp = fopen(_STR_BMP, "wb")) == NULL) //练习就写的简单一点
        ERR_EXIT("fopen error!");
    // 文件写入
    fwrite(mask, sizeof(GLubyte), len, bmp);

    fclose(bmp);;
}

// 屏幕绘制函数
void
display(void)
{
    GLubyte mask[sizeof __masks];
    //先读取文件中内容

    FILE *bmp = NULL;
    if ((bmp = fopen(_STR_BMP, "rb")) == NULL) //练习就写的简单一点
        ERR_EXIT("fopen error!");
    // 文件写入
    fread(mask, sizeof(GLubyte), sizeof mask, bmp);

    fclose(bmp);

    //这里开始绘制
    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_POLYGON_STIPPLE); //启动镂空模式
    glPolygonStipple(mask); //设置镂空的样式

    glRectf(-0.5f,-0.5f,0.0f,0.0f); // 在左下方绘制一个有镂空效果的正方形
    glDisable(GL_POLYGON_STIPPLE); //关闭镂空模式
    glRectf(0.0f,0.0f,0.5f,0.5f); //往右上方绘制一个无镂空效果的正方形

    glFlush();

}

最后的效果图如下:

小结

本课学习了绘制几何图形的一些细节。

点可以设置大小。

直线可以设置宽度;可以将直线画成虚线。

多边形的两个面的绘制方法可以分别设置;在三维空间中,不可见的多边形可以被剔除;可以将填充多边形绘制成镂空的样式。

了解这些细节会使我们在一些图象绘制中更加得心应手。

另外,把一些数据写到程序之外的文件中,并用专门的工具编辑之,有时可以显得更方便。

做为一个菜鸟,欢迎交流指正.希望此刻更有趣.加油.

时间: 2024-10-02 11:36:23

opengl基础学习专题 (三) 多边形绘制的几种样式的相关文章

opengl基础学习专题 (二) 点直线和多边形

题外话 随着学习的增长,越来越觉得自己很水.关于上一篇博文中推荐用一个 学习opengl的 基于VS2015的 simplec框架.存在 一些问题. 1.这个框架基于VS 的Debug 模式下,没有考虑Release版本 2.这个版本中chead,c基础头文件中有些宏设计的不好,例如 //4.0 控制台打印错误信息 #ifndef CERR #define CERR(fmt,...) fprintf(stderr,fmt,##__VA_ARGS__),putchar('\n') #endif/*

opengl基础学习专题 (一 )编程环境搭建

题外话: 第一次在博客园上同大家分享博文.水的的地方,错别字的地方.环境交流.批评.知道了马上改. 以前在百度空间中写技术分享博文,后来百度啥也没说就把整个空间封了.当时感觉 还是有点寒心.只想黑一下它,厂长老了.场子也该换了. 对于想写个oepngl基础学习的专题,主要是网上资源有点老,好多都运行不了,买了红宝书<<OpenGL 编程指南 第8版>>,看了还看,觉得有点专业,优化和技术核心讲的的多. 而且都是代码片段,像自己这种门外汉学习起来还是觉得基础不行.需要恶补一下关于 f

javascript基础学习(三)

javascript之运算符 学习要点: 表达式 运算符:一元运算符,算术运算符,关系运算符,逻辑运算符,*位运算符,赋值运算符 一.表达式 表达式有常量表达式,变量表达式,复合表达式. 二.算术运算符 一元:+(正号).-(负号).++.—— 二元:*./.%.+.- 三.关系运算符 ==.=== ps:等同运算符比相等运算符要求更为严格,等同运算符只有在两个操作数类型相同,并且值也相同的情况下才会返回true. <.<=.>.>= instanceof运算符.       in

蓝鸥零基础学习HTML5第六讲 CSS的常见样式

蓝鸥零基础学习HTML5第六讲 CSS的常见样式 CSS的常见样式 1.css文本样式的设置 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> div { font-size:160px; font-family: "sans-Seri

JAVA基础学习-集合三-Map、HashMap,TreeMap与常用API

一.Map简述 1.1.简述 public interface Map<K,V> 类型参数: K - 此映射所维护的键的类型 key V - 映射值的类型 value 该集合提供键--值的映射.key不能重复,一对对的存储方式 将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值. 1.2.方法 嵌套类摘要 static interface Map.Entry<K,V> 映射项(键-值对). 方法摘要 void clear() 从此映射中移除所有映射关系(可选操

java网络爬虫基础学习(三)

尝试直接请求URL获取资源 豆瓣电影 https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=time&page_limit=20&page_start=0 浏览器打开该地址: 发现是这样的 在这里我们需要用java抓取电影的信息,首先要找到资源链接,浏览器右键->检查打开谷歌调试工具 我们可以看到下图 有很多的资源请求,在这里我是一个个搜索,看那个是电影信息的Headers 发

salesforce lightning零基础学习(七) 列表展示数据时两种自定义编辑页面

上一篇Lightning内容描述的是LDS,通过LDS可以很方便的实例化一个对象的数据信息.当我们通过列表展示数据需要编辑时,我们常使用两种方式去处理编辑页面:Pop Up Window弹出修改详情以及在本页面隐藏详情页面显示编辑页面. 实现这个功能以前主要需要先了解几个标签: lightning:recordForm: 此标签允许用户快速的创建一个form去查看,添加以及修改一条记录.集合了 lightning:recordEditForm 以及 lightning:recordViewFor

openGL学习笔记三 : 绘制点、线以及多边形

前面我们绘制了一个矩形, 现在就一并来看看如何绘制点.线以及多边形吧. 一. 点 在openGL里面有这样一系列的函数, 他们以glVertex开头, 没错, 这就是我们需要的绘制点的函数, 来看看有哪些: WINGDIAPI void APIENTRY glVertex2d (GLdouble x, GLdouble y); WINGDIAPI void APIENTRY glVertex2dv (const GLdouble *v); WINGDIAPI void APIENTRY glVe

[Python学习] 专题三.字符串的基础知识

        在Python中最重要的数据类型包括字符串.列表.元组和字典等.该篇主要讲述Python的字符串基础知识. 一.字符串基础         字符串指一有序的字符序列集合,用单引号.双引号.三重(单双均可)引号引起来.如: s1='www.csdn.net'   s2="www.csdn.net"   s3='''aaabbb''' 其中字符串又包括: 1.转义字符串 像C语言中定义了一些字母前加"\"来表示常见的那些不能显示的ASCII字符,pyth