最近在看提高C++性能的编程技术一书,居然发现里面有三章是专门讲述内联的。看来内联在作者心中占据了非常重要的位置,内联在作者心里是最有效的改进性能的策略了。

阅读完这三章内容之后,我对内联的理解更加模糊了。因为到最后作者也说你内联性能不一定变好,你不内联性能也不一定变坏。能跟内联牵扯上关系的东西太多了。

我记得有人曾经问过我这个问题,内联和宏替换的区别是什么。当时我还是大三的时候,准备找个实习,是电面的时候被别人问到的。还是国内著名的IT企业,我就这样的水水的跟人家乱扯。现在回忆起来我好像什么也没说出来的样子。说实话,我那个时候感觉内联和宏替换区别不大,一样的对待也没什么大不了的。第三次电面的时候我就彻底悲剧了,最主要的原因还是因为C++功底不扎实。想想那个时候也挺可爱的,把简历写的那么好笑那么烂,他们居然也给我机会进到最后一轮。最后一轮我实在表现的太烂了。

那么区别到底是什么了。我说说现在的理解吧。首先,宏替换不能算编译器的时候,应该算预处理器的工作,而内联属于编译器的工作,阶段都不同了。其次,宏替换很白痴就是简单的应用些规则进行代码替换而已,而内联是很高级的功能吧,inline只是对编译器的指示,内联不内联是编译器自己的决定,有可能你不写inline函数也被内联了,不过我觉得可能性比较小,而且内联不仅仅是在调用位置替换上被调用函数的代码,编译器还会对替换之后的新代码整个进行优化,也就是能够跨越函数调用进行优化,这一点据说是内联提高性能的关键。最后,宏的可读性很差,什么机制都没有,而内联就不同,函数还是函数,参数和返回类型,编译器对函数的检查等等都还在,而宏就没有这些优待。我能说出来的也就这么些了。。。

关于什么是内联的话应该就不需要我进行解释了。内联能避免函数调用的代价这一点也不需要解释了。函数调用的代价说简单点就是保存现场,主要是一些寄存器的内容,一般会把这些寄存器的内容压栈保存起来,还有就是传递参数,参数的传递是通过改变堆栈指针达到的。函数调用完成之后还需要恢复现场,就是从栈里面恢复一些寄存器的内容,还有恢复原来的栈指针。但是,具体起来函数调用需要做些什么还是比较难说清楚的,不同的体系结构做的事情都不一样,再说写c++代码的时候谁又想过多的考虑底层具体发生了什么了。

那么来思考下为什么要使用内联和为什么又不要使用内联吧。大家都知道函数调用会造成一定的代价,既然内联可以避免这个代价那么为什么我们不用内联了。好吧,那么我们就用内联把函数调用的代价消去。那么为什么我们只内联比较小的函数了,而超过一定规模的函数就不内联了。首先,如果一个函数规模较大,那么函数调用的代价在整个代价里面的比例就较小了。其次,内联较大规模的函数会造成编译后的二进制代码增大,如果函数非叶子位置函数(只会被别的函数调用的函数)那么这个可能性更大,很可能就会到达无法忍受的地步,内联造成编译时间变长也是一方面。最后,内联也会损失某种性能,比如缓存和页交换之类的,如果是函数调用,机器可以运用指令和数据缓存之类的,但是如果是内联,那么不同的调用位置其在映像里面的位置是不一样的,那么机器是不会缓存的。所以,内联也不是能随便用的。

其实说了这么多了,我们还是不知道什么时候该内联什么时候不该内联。我们只了解到,如果函数足够小就内联吧,那么小的标准是什么,代码五行以内没有循环?什么时候不该内联,函数规模太大了?反正内联是一件太纠结的事情了,有可能你用编译器测试一下内联和不内联性能是没有变化的,因为可能编译器在幕后就内联了。

根据我所看的这本书的说法,我有一些建议。对少于五行的代码和调用频率大于80%的函数一定内联,其余的你还是仔细考虑下吧。你可以再分解调用频率大于80%的函数,使其规模变小,然后再考虑是否内联它。其实,这本书的思想还是对整个系统进行测试之后才进行内联选择,毕竟性能优化也是最后阶段的事情。你需要找出系统执行的关键路径,其实也就是执行频率最高的一些函数,根据整个系统的流程图,你就会发现一条系统执行频率最高的路径,内联应该只发生在这条路径上面。

好了,这就是我对内联的一些理解,这本书关于内联的内容实在太多了,我觉得还是下次再写一篇文章扯扯吧。

如果一个内存池需要线程同步了,估计和默认的内存操作也差不了多远了。现在对内存操作的优化只是在优化线程同步操作上面了。默认的lock和unlock可能实现得过于完美,因而要求更多的cpu周期。如果选择更原子的lock和unlock实现,还是可以加快内存操作的速度的。

多线程内存池在实现上也就是在申请和释放外面包裹了一对加锁和解锁操作而已。

如果我们采用模板编程,就可以实现一个功能强悍的模板类,该模板类有两个参数,单线程内存池和锁。这样的话,可以组合出很多情况。比如说,单线程内存池有对象大小固定和不固定两种实现,锁在Windows下可以用临界区,互斥体,信号量,事件实现。这样最多可以组合出8种不同的实例化。

一般说来互斥体比临界区慢很多,在这里可以进行很好的测试。我实现了临界区和互斥体版本的锁,经过测试发现前者比后者快30多倍。因为互斥体据说是内核对象,而临界区是用户态对象,那么互斥体的使用就需要系统在用户态和内核态之间进行切换,肯定会消耗更多的时间。

互斥体版本代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>

template <typename T> class MemoryPool
{
public:
MemoryPool(size_t size = EXPANSION_SIZE)
{
expandTheFreeList(size);
}
~MemoryPool();
//allocate a T element from the free list.
void* alloc(size_t size)
{
if (next == NULL)
{
expandTheFreeList();
}
MemoryPool<T>* head = next;
next = head->next;
return head;
}
//return a T element to the free list.
void free(void* doomed)
{
MemoryPool<T>* head = static_cast< MemoryPool<T>* > (doomed);
head->next = next;
next = head;
}

private:
//next element on the free list.
MemoryPool<T>* next;
//if the freelist is empty, expand it by this amount.
enum {EXPANSION_SIZE = 32};
//add free elements to the free list
void expandTheFreeList(int howMany = EXPANSION_SIZE);
};

template <typename T> MemoryPool<T> :: ~MemoryPool()
{
MemoryPool<T>* nextPtr = NULL;
while (nextPtr)
{
nextPtr = next;
next = next->next;
delete [] nextPtr;
}
}

template <typename T> void MemoryPool<T> :: expandTheFreeList(int howMany)
{
//we must allocate an object enough to contain the next pointer
size_t size = sizeof(T) > sizeof(MemoryPool<T>*) ? sizeof(T) :
sizeof(MemoryPool<T>*);

MemoryPool<T>* runner = (MemoryPool<T>*) new char[size];
next = runner;
for (int i = 0; i < howMany; ++i)
{
runner->next = (MemoryPool<T>*) new char[size];
runner = runner->next;
}
runner->next = NULL;
}

class ABClock //abstract base class
{
public:
virtual ~ABClock() {}
virtual void lock() = 0;
virtual void unlock() = 0;
};

class MutexLock : public ABClock
{
public:
MutexLock()
{
hMutex = CreateMutex(NULL, FALSE, NULL);
}
~MutexLock()
{
CloseHandle(hMutex);
}
void lock()
{
WaitForSingleObject(hMutex, INFINITE);
}
void unlock()
{
ReleaseMutex(hMutex);
}
private:
HANDLE hMutex;
};

template <typename POOLTYPE, typename LOCK>
class MTMemoryPool
{
public:
//allocate an element from the freelist.
void* alloc(size_t size)
{
void* mem;
theLock.lock();
mem = stPool.alloc(size);
theLock.unlock();
return mem;
}

//return an element to the freelist
void free(void* someElement)
{
theLock.lock();
stPool.free(someElement);
theLock.unlock();
}

private:
POOLTYPE stPool;//Single-threaded pool.
LOCK theLock;
};

class Rational
{
public:
Rational(int a = 0, int b = 1) : n(a), d(b) {}
void* operator new(size_t size)
{
return memPool->alloc(size);
}
void operator delete(void* doomed, size_t size)
{
memPool->free(doomed);
}
static void newMemPool()
{
memPool = new MTMemoryPool< MemoryPool<Rational>, MutexLock>;
}
static void deleteMemPool()
{
delete memPool;
}

private:
int n;
int d;
static MTMemoryPool< MemoryPool<Rational>, MutexLock>* memPool;
};
MTMemoryPool< MemoryPool<Rational>, MutexLock>* Rational::memPool = NULL;

int main()
{
const int ARRAY_SIZE = 1000;
const int LOOP_TIMES = 5000;

Rational* array[ARRAY_SIZE];
clock_t beg = clock();
Rational::newMemPool();
for (int i = 0; i < LOOP_TIMES; ++i)
{
for (int j = 0; j < ARRAY_SIZE; ++j)
{
array[j] = new Rational(j);
}
for (int j = 0; j < ARRAY_SIZE; ++j)
{
delete array[j];
}
}
clock_t end = clock();
printf("use %f second(s).\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
system("pause");
Rational::deleteMemPool();

return 0;
}

运行结果,

临界区版本代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>

template <typename T> class MemoryPool
{
public:
MemoryPool(size_t size = EXPANSION_SIZE)
{
expandTheFreeList(size);
}
~MemoryPool();
//allocate a T element from the free list.
void* alloc(size_t size)
{
if (next == NULL)
{
expandTheFreeList();
}
MemoryPool<T>* head = next;
next = head->next;
return head;
}
//return a T element to the free list.
void free(void* doomed)
{
MemoryPool<T>* head = static_cast< MemoryPool<T>* > (doomed);
head->next = next;
next = head;
}

private:
//next element on the free list.
MemoryPool<T>* next;
//if the freelist is empty, expand it by this amount.
enum {EXPANSION_SIZE = 32};
//add free elements to the free list
void expandTheFreeList(int howMany = EXPANSION_SIZE);
};

template <typename T> MemoryPool<T> :: ~MemoryPool()
{
MemoryPool<T>* nextPtr = NULL;
while (nextPtr)
{
nextPtr = next;
next = next->next;
delete [] nextPtr;
}
}

template <typename T> void MemoryPool<T> :: expandTheFreeList(int howMany)
{
//we must allocate an object enough to contain the next pointer
size_t size = sizeof(T) > sizeof(MemoryPool<T>*) ? sizeof(T) :
sizeof(MemoryPool<T>*);

MemoryPool<T>* runner = (MemoryPool<T>*) new char[size];
next = runner;
for (int i = 0; i < howMany; ++i)
{
runner->next = (MemoryPool<T>*) new char[size];
runner = runner->next;
}
runner->next = NULL;
}

class ABClock //abstract base class
{
public:
virtual ~ABClock() {}
virtual void lock() = 0;
virtual void unlock() = 0;
};

class CriticalSectionLock : public ABClock
{
public:
CriticalSectionLock()
{
InitializeCriticalSection(csMyCriticalSection);
}
~CriticalSectionLock()
{
DeleteCriticalSection(csMyCriticalSection);
}
void lock()
{
EnterCriticalSection(csMyCriticalSection);
}
void unlock()
{
LeaveCriticalSection(csMyCriticalSection);
}
private:
CRITICAL_SECTION csMyCriticalSection;
};

template <typename POOLTYPE, typename LOCK>
class MTMemoryPool
{
public:
//allocate an element from the freelist.
void* alloc(size_t size)
{
void* mem;
theLock.lock();
mem = stPool.alloc(size);
theLock.unlock();
return mem;
}

//return an element to the freelist
void free(void* someElement)
{
theLock.lock();
stPool.free(someElement);
theLock.unlock();
}

private:
POOLTYPE stPool;//Single-threaded pool.
LOCK theLock;
};

class Rational
{
public:
Rational(int a = 0, int b = 1) : n(a), d(b) {}
void* operator new(size_t size)
{
return memPool->alloc(size);
}
void operator delete(void* doomed, size_t size)
{
memPool->free(doomed);
}
static void newMemPool()
{
memPool = new MTMemoryPool< MemoryPool<Rational>, CriticalSectionLock>;
}
static void deleteMemPool()
{
delete memPool;
}

private:
int n;
int d;
static MTMemoryPool< MemoryPool<Rational>, CriticalSectionLock>* memPool;
};
MTMemoryPool< MemoryPool<Rational>, CriticalSectionLock>* Rational::memPool = NULL;

int main()
{
const int ARRAY_SIZE = 1000;
const int LOOP_TIMES = 5000;

Rational* array[ARRAY_SIZE];
clock_t beg = clock();
Rational::newMemPool();
for (int i = 0; i < LOOP_TIMES; ++i)
{
for (int j = 0; j < ARRAY_SIZE; ++j)
{
array[j] = new Rational(j);
}
for (int j = 0; j < ARRAY_SIZE; ++j)
{
delete array[j];
}
}
clock_t end = clock();
printf("use %f second(s).\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
system("pause");
Rational::deleteMemPool();

return 0;
}

运行结果,

两个版本的代码只在线程同步锁的实现上有差别,但是速度却相差了30多倍。

很多系统中申请的内存大小都不是固定的,而是变化的,比如一个http服务器。http服务器获得的请求不同,不同的请求需要不同大小的内存块。那么如何构造一个可以申请不同大小内存的内存池了。其实,构造方法类似vs2008下面的debug模式中的全局new和delete的实现。思路大致是申请很多不同大小的内存块,链接起来,根据需求从内存块链的头部申请指定大小内存。
注意,本文代码中的内存池只在内存块链的头部申请内存,而且从不进行内存释放,一直到内存池使用完毕。这样的设计确实可以尽可能得提高全局的速度,只要能保证系统的使用过程中不会把内存耗光就行了。不过,我觉得如果耗光内存的可能性还是太大了。但是,你想想实现一个允许对象大小可变的内存池本来就不那么容易,而且得在充分考虑效率的前提下了。如果效率太差了,为什么不直接使用默认的new和delete表达式了。
不过,其实有一个更好的办法,我们只使用内存池一段时间,就马上删除这个内存池,再重新申请一次内存池。也就是我们不能一次使用内存池的时间太长了。困难的是我们如何确定一次使用内存池的时间,什么需要删除内存池,时间太久了也许内存会耗光的,删除频繁了点又得不到想要的效率。这些也许就需要具体情况具体对待了,也许这个内存池的实现根本不能用。

内存池代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

class MemoryChunk
{
public:
MemoryChunk(MemoryChunk* nextChunk, size_t reqSize)
{
chunkSize = reqSize > DEFAULT_CHUNK_SIZE ? reqSize :
DEFAULT_CHUNK_SIZE;
next = nextChunk;
byteAlreadyAllocated = 0;
mem = new char[chunkSize];
}

~MemoryChunk()
{
delete [] mem;
}

void* alloc(size_t size)
{
void* addr = (void*)(mem + byteAlreadyAllocated);
byteAlreadyAllocated += size;
return addr;
}

//不需要释放内存块
void free(void* doomed)
{

}

MemoryChunk* nextMemChunk() { return next; }
size_t spaceAvailable()
{
return chunkSize - byteAlreadyAllocated;
}
enum {DEFAULT_CHUNK_SIZE = 4096};
private:
MemoryChunk* next;//下一个内存块
char* mem;//该内存块地址
size_t chunkSize; //该内存块的大小
size_t byteAlreadyAllocated;//该内存块被申请使用的大小
};

class ByteMemoryPool
{
public:
ByteMemoryPool(size_t initSize = MemoryChunk::DEFAULT_CHUNK_SIZE)
{
expandStorage(initSize);
}
~ByteMemoryPool();

void* alloc(size_t reqSize)
{
size_t space = listOfMemoryChunks->spaceAvailable();
if (space < reqSize)
{
expandStorage(reqSize);
}
return listOfMemoryChunks->alloc(reqSize);
}

void free(void* doomed)
{
listOfMemoryChunks->free(doomed);
}

private:
void expandStorage(size_t reqSize)
{
listOfMemoryChunks = new MemoryChunk(listOfMemoryChunks, reqSize);
}
MemoryChunk* listOfMemoryChunks;
};

ByteMemoryPool::~ByteMemoryPool()
{
MemoryChunk* memChunk = listOfMemoryChunks;
while (memChunk)
{
listOfMemoryChunks = memChunk->nextMemChunk();
delete memChunk;
memChunk = listOfMemoryChunks;
}
}

class Rational
{
public:
Rational(int a = 0, int b = 1) : n(a), d(b) {}
void* operator new(size_t size) {return memPool->alloc(size);}
void operator delete(void* doomed, size_t size) {/*memPool->free(doomed);*/}
static void newMemPool() {memPool = new ByteMemoryPool;}
static void deleteMemPool() {delete memPool;}

private:
int n;
int d;
static ByteMemoryPool* memPool;
};
ByteMemoryPool* Rational::memPool = NULL;

int main()
{
const int ARRAY_SIZE = 1000;
const int LOOP_TIMES = 5000;

Rational* array[ARRAY_SIZE];
clock_t beg = clock();
Rational::newMemPool();
for (int i = 0; i < LOOP_TIMES; ++i)
{
for (int j = 0; j < ARRAY_SIZE; ++j)
{
array[j] = new Rational(j);
}
for (int j = 0; j < ARRAY_SIZE; ++j)
{
delete array[j];
}
}
clock_t end = clock();
printf("use %f second(s).\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
system("pause");
Rational::deleteMemPool();

return 0;
}

运行效果,
与上一篇文章里面的二个内存池实现的运行时间相比,没有多大差别。

你可以好好的阅读下代码,或者自己重新敲一遍,就会发现这个内存池的实现确实简单明了,没有一点冗余的代码,但是又非常高效。如果我们采用使用一段时间就重新初始化内存池的方式就不会耗光内存,不过恰当的使用该内存池也不是件容易的事情。其实,我主要还是觉得这个版本的内存池实现主要应该用在一次的业务处理时间不会太长,或者说内存申请量不会太多的场景中

本文的思路和代码主要来自提高C++性能的编程技术一书。

内存池并不是表面意义上存储大量可分配内存的池子,如果是这样的话,new和delete对应的就是一个理论上无限大的内存池。标题中的单线程指的是该内存池实现为了加快速度没有处理线程同步,因为很多应用你明显知道不需要线程同步。固定大小指的是该内存池每次只分配或者删除固定大小的对象。

这样的内存池怎么就加快了速度了。我觉得主要有以下几个方面。

1.不需要处理线程同步,去除掉线程同步的代码后,显然速度会加快。

2.总体来看,该内存池只会扩张不会收缩,也就是向系统释放堆内存的次数减少了。事实上,该内存池总是按需操作的。

3.该内存池的new和delete操作并没有去真正申请和释放堆内存,而是向自定义的对象空闲列表申请和释放。当对象空闲列表为空的时候,才一次性申请一大片空闲对象。大家都知道堆申请和释放比较耗时,最快的栈内存。所以,这也是一个显著的方面。

以下2段代码分别展示了原始的内存操作和使用内存池后的操作,

原始情况,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include
#include
#include

class Rational
{
public:
Rational(int a = 0, int b = 1) : n(a), d(b) {}

private:
int n; // Numerator
int d; // Denominator
};

int main()
{
const int ARRAY_SIZE = 1000;
const int LOOP_TIMES = 5000;
Rational* array[ARRAY_SIZE];

clock_t beg = clock();
for (int i = 0; i < LOOP_TIMES; ++i)
{
for (int j = 0; j < ARRAY_SIZE; ++j)
{
array[j] = new Rational(j);
}
for (int j = 0; j < ARRAY_SIZE; ++j)
{
delete array[j];
}
}
clock_t end = clock();
printf("use %f second(s).\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
system("pause");

return 0;
}

该代码的运行结果,

内存池,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

struct NextOnFreeList
{
NextOnFreeList* next;
};

class Rational
{
public:
Rational(int a = 0, int b = 1) : n(a), d(b) {}

void* operator new(size_t size)
{
if (freeList == 0)// if the list is empty, fill it up.
{
expandTheFreeList();
}

NextOnFreeList* head = freeList;
freeList = head->next;
return head;
}

void operator delete(void* doomed, size_t size)
{
NextOnFreeList* head = static_cast<NextOnFreeList*> (doomed);
head->next = freeList;
freeList = head;
}
static void newMemPool()
{
expandTheFreeList();
}
static void deleteMemPool();

private:
static void expandTheFreeList();
static NextOnFreeList* freeList;
enum { EXPANSION_SIZE = 32 };
int n; // Numerator
int d; // Denominator
};

NextOnFreeList* Rational::freeList = NULL;

//当空闲列表里面没有对象时,该函数才会被调用
void Rational::expandTheFreeList()
{
size_t size = sizeof(Rational) > sizeof(NextOnFreeList*) ?
sizeof(Rational) : sizeof(NextOnFreeList*);
NextOnFreeList* runner = (NextOnFreeList*)new char[size];
freeList = runner;
for (int i = 0; i < EXPANSION_SIZE; ++i)
{
runner->next = (NextOnFreeList*)new char[size];
runner = runner->next;
}
runner->next = 0;
}

void Rational::deleteMemPool()
{
NextOnFreeList* nextPtr = freeList;
while (nextPtr)
{
freeList = nextPtr->next;
delete [] nextPtr;
nextPtr = freeList;
}
}

int main()
{
const int ARRAY_SIZE = 1000;
const int LOOP_TIMES = 5000;
Rational* array[ARRAY_SIZE];

clock_t beg = clock();
Rational::newMemPool();
for (int i = 0; i < LOOP_TIMES; ++i)
{
for (int j = 0; j < ARRAY_SIZE; ++j)
{
array[j] = new Rational(j);
}
for (int j = 0; j < ARRAY_SIZE; ++j)
{
delete array[j];
}
}
clock_t end = clock();
printf("use %f second(s).\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
system("pause");
Rational::deleteMemPool();

return 0;
}

该代码运行结果,

通过以上结果的对比可以看到时间比例为921/78,直接就快了10多倍。而且我们的内存申请和删除操作还不够频繁,如果再更频繁的场合,效率上面的提示会更加大。

下面我们来分析一下内存池实现的代码。

说来也比较简单,因为原理都已经讲清楚了。首先去除了线程同步代码,然后把内存放在对象空闲列表里面,从里面释放和删除对象,当对象空闲列表为空的时候,一次性申请一片对象。作为一个有经验的c++程序员这些代码已经足够简单了。需要注意的是,在expandTheFreeList函数里面,申请新对象的时候,需要注意大小必须是Rational和NextOnFreeList*中大小大的一个。该内存池还用到了指针类型强转,语法上面还是比较巧妙的。

由于该内存池只是针对单个的类,应用场合未免有点限制。如果我们用泛型编程实现内存池,就可以把应用场合推广了。下面将内存池的功能独立出来,用模板重新实现了下,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include
#include
#include

template class MemoryPool
{
public:
MemoryPool(size_t size = EXPANSION_SIZE)
{
expandTheFreeList(size);
}
~MemoryPool();
//allocate a T element from the free list.
void* alloc(size_t size)
{
if (next == NULL)
{
expandTheFreeList();
}
MemoryPool* head = next;
next = head->next;
return head;
}
//return a T element to the free list.
void free(void* doomed)
{
MemoryPool* head = static_cast< MemoryPool* > (doomed);
head->next = next;
next = head;
}

private:
//next element on the free list.
MemoryPool* next;
//if the freelist is empty, expand it by this amount.
enum {EXPANSION_SIZE = 32};
//add free elements to the free list
void expandTheFreeList(int howMany = EXPANSION_SIZE);
};

template MemoryPool :: ~MemoryPool()
{
MemoryPool* nextPtr = NULL;
while (nextPtr)
{
nextPtr = next;
next = next->next;
delete [] nextPtr;
}
}

template void MemoryPool :: expandTheFreeList(int howMany)
{
//we must allocate an object enough to contain the next pointer
size_t size = sizeof(T) > sizeof(MemoryPool*) ? sizeof(T) :
sizeof(MemoryPool*);

MemoryPool* runner = (MemoryPool*) new char[size];
next = runner;
for (int i = 0; i < howMany; ++i) { runner->next = (MemoryPool*) new char[size];
runner = runner->next;
}
runner->next = NULL;
}

class Rational
{
public:
Rational(int a = 0, int b = 1) : n(a), d(b) {}
void* operator new(size_t size) {return memPool->alloc(size);}
void operator delete(void* doomed, size_t size) {memPool->free(doomed);}
static void newMemPool() {memPool = new MemoryPool;}
static void deleteMemPool() {delete memPool;}

private:
int n;
int d;
static MemoryPool* memPool;
};
MemoryPool* Rational::memPool = NULL;

int main()
{
const int ARRAY_SIZE = 1000;
const int LOOP_TIMES = 5000;
Rational* array[ARRAY_SIZE];

clock_t beg = clock();
Rational::newMemPool();
for (int i = 0; i < LOOP_TIMES; ++i)
{
for (int j = 0; j < ARRAY_SIZE; ++j)
{
array[j] = new Rational(j);
}
for (int j = 0; j < ARRAY_SIZE; ++j)
{
delete array[j];
}
}
clock_t end = clock();
printf("use %f second(s).\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
system("pause");
Rational::deleteMemPool();

return 0;
}

运行效果如图:

可以看到用模板重新实现后,速度并没有下降多少,这个例子速度下降近一倍的原因估计是与原来的实现相比多了一些函数调用吧,但是还是比不使用内存池的情况下快一个数量级。

整个代码的思路是把内存池的实现部分用一个模板类包含了,用不同的类实例化模板类,就可以对不同的类使用内存池了,泛型编程在速度和灵活性上面的结合确实是非常优美的。而且你可以看到,例子二和例子三的主函数部分完全一致,也就是保持了外部接口的统一性,只需要修改类Rational的实现即可。现在将内存池应用到不同的类也只需要在类里面添加一个针对该类的泛型内存池成员而已。

假设在一个线程同步环境中,有类似下面所示的代码段:

//进入线程同步

nNum++;

//退出线程同步

以win32为例,如我们所知,线程同步工具有临界区,互斥体,信号量。我们可以任意选择一个,为了简单很可能我们就选择了临界区。假如我们需要同步的代码非常简单,我非常建议不需要使用c++的任何功能。但是,很可能没这么幸运,很可能你的代码会被很多人修改,很可能同步的时候需要异常退出,很可能同步的里面还有点逻辑处理,很可能你在退出的时候没有释放锁。那么你就悲剧了。

C++在这里一点上就体现出了与生俱来的优势,构造函数和析构函数。假如我们把获得锁写进构造函数里面,释放锁写进析构函数里面,就能保证任意情况的退出之前都能够释放锁,从而不会造成死锁。

还有个问题是我们应该如何实现这些不同的同步工具。如果,我们分别写三个类,CCriticalSection,CMutex,CSemaphore,这三个类不是继承自同一个基类的,而且这三个类里面也没有任何的虚函数,而且我们把操作都写成内联的。那么,在效率上与直接用C相比基本没有差别。因为,所有额外的调用开销都内联了。在不丧失效率的同时保持了灵活性,这确实是非常强大的地方。

万一你说为了灵活性,需要让这三个类继承同一个基类,CBaseLock,同时需要把析构函数定义成虚的,这样就会造成很多额外的性能损失。比如,可能需要构造和析构基类对象,需要初始化虚函数指针,执行阶段需要通过虚函数指针间接调用,还有一个最重要的损失,编译器无法内联虚函数,如果虚函数比较小,那么相对于原来的情况,这是一个巨大的性能损失。某种意义上,我们可以忽视,基类对象的构造和析构,以及虚函数指针的初始化和调用时刻的间接寻址,因为这些可以其它方面的因素来抵消。

但是,无法内联一个小的函数所造成的额外开销是很大的。想象一下,比如刚才几个类里面的析构函数只是释放锁这一个操作,肯定是可以内联的,但是现在写成虚函数了,就无法内联了,如果这个函数被调用很多次,比如几百万次,和原来消耗的时候相比,肯定是天壤之别了,因为原来没有函数调用开销,现在有,而且因为函数内部操作太少了,函数调用开销所占据的比例非常大,所以你的设计造成了巨大的性能损失。

本文的观点主要来自提高C++性能的编程技术一书,欢迎吐槽。

5.6.2 网格场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;

void myInit()
{
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
}

//draw a z-axis, with cone at end
void axis(double length)
{
glPushMatrix();
glBegin(GL_LINES);
glVertex3d(0, 0, 0);
glVertex3d(0, 0, length);
glEnd();
glTranslated(0, 0, length - 0.2);//平移
glutWireCone(0.04, 0.2, 12, 9);
glPopMatrix();
}

void displayWire()
{
glMatrixMode(GL_PROJECTION);//设置视景体
glLoadIdentity();
glOrtho(-2.0 * 64 / 48, 2.0 * 64 / 48, -2.0, 2.0, 0.1, 100);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(1.0, 1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

glClear(GL_COLOR_BUFFER_BIT);
glColor3d(0, 0, 0);

axis(0.5);//z-axis
glPushMatrix();
glRotated(-90, 1.0, 0, 0);
axis(0.5);//y-axis
glRotated(90.0, 0, 1.0, 0);
axis(0.5);//x-axis
glPopMatrix();

glPushMatrix();
glTranslated(0.5, 0.5, 0.5);
glutWireCube(1.0);//线框立方体
glPopMatrix();

glColor3d(1.0, 0, 0);
glPushMatrix();
glTranslated(1.0, 1.0, 0);
glutWireSphere(0.25, 10, 8);//线框球体
glPopMatrix();

glColor3d(0, 1.0, 0);
glPushMatrix();
glTranslated(1.0, 0, 1.0);
glutWireCone(0.2, 0.5, 10, 8);//线框圆锥体
glPopMatrix();

glColor3d(0, 0, 1.0);
glPushMatrix();
glTranslated(1.0, 1.0, 1.0);
glutWireTeapot(0.2);//线框茶壶
glPopMatrix();

glColor3d(1.0, 0, 1.00);
glPushMatrix();
glTranslated(0, 1.0, 0);
glutWireTorus(0.1, 0.3, 10, 10);//线框花环
glPopMatrix();

glColor3d(0, 1.0, 1.0);
glPushMatrix();
glTranslated(1.0, 0, 0);
glScaled(0.15, 0.15, 0.15);
glutWireDodecahedron();//线框12面体
glPopMatrix();

glColor3d(0.1, 0.5, 0.2);
glPushMatrix();
glTranslated(0, 1.0, 1.0);
glutWireCube(0.25);
glPopMatrix();

glPushMatrix();
glTranslated(0, 0, 1.0);
GLUquadricObj* qobj = gluNewQuadric();//创建二次曲面对象
gluQuadricDrawStyle(qobj, GLU_LINE);
glColor3d(0.3, 0.5, 0.4);
gluCylinder(qobj, 0.2, 0.2, 0.4, 8, 8);//圆柱体
glPopMatrix();
glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("Transformation Test -Wireframes");
glutDisplayFunc(displayWire);
myInit();
glutMainLoop();//进入消息循环

return 0;
}

5.6.3 着色三维场景绘制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;

void myInit()
{
glEnable(GL_LIGHTING);//enable the light source
glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH);//光滑着色
glEnable(GL_DEPTH_TEST);//启用深度测试,根据坐标的远近自动隐藏被遮住的图形
glEnable(GL_NORMALIZE);//根据函数glNormal的设置条件,启用法向量
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
}

//draw thin wall with top = xz-plane, corner at origin
void wall(double thickness)
{
glPushMatrix();
glTranslated(0.5, 0.5 * thickness, 0.5);
glScaled(1.0, thickness, 1.0);
glutSolidCube(1.0);
glPopMatrix();
}

//table leg
void tableLeg(double thick, double len)
{
glPushMatrix();
glTranslated(0, len / 2, 0);
glScaled(thick, len, thick);
glutSolidCube(1.0);
glPopMatrix();
}

//draw one axis of the unit jack-a stretched sphere
void jackPart()
{
glPushMatrix();
glScaled(0.2, 0.2, 1.0);
glutSolidSphere(1, 15, 15);
glPopMatrix();

glPushMatrix();
glTranslated(0, 0, 1.2);//ball on one end
glutSolidSphere(0.2, 15, 15);
glTranslated(0, 0, -2.4);
glutSolidSphere(0.2, 15, 15);//ball on the other end
glPopMatrix();
}

//draw a unit jact out of spheroids
void jack()
{
glPushMatrix();
jackPart();
glRotated(90.0, 0, 1, 0);
jackPart();
glRotated(90.0, 1.0, 0, 0);
jackPart();
glPopMatrix();
}

//draw the table - a top and four legs
//绘画桌子时候,外部调用已经使原点到达桌子中间了
void table(double topWid, double topThick, double legThick, double legLen)
{
glPushMatrix();
glTranslated(0, legLen, 0);
glScaled(topWid, topThick, topWid);
glutSolidCube(1.0);//桌面
glPopMatrix();

double dist = 0.95 * topWid / 2.0 - legThick / 2.0;
glPushMatrix();
//glTranslated(dist, 0, dist);
glTranslated(dist, 0, dist);
tableLeg(legThick, legLen);
glTranslated(0, 0, -2 * dist);
tableLeg(legThick, legLen);
glTranslated(-2 * dist, 0, 2 * dist);
tableLeg(legThick, legLen);
glTranslated(0, 0, -2 * dist);
tableLeg(legThick, legLen);
glPopMatrix();
}

void displaySolid()
{
//设置表面纹理属性
GLfloat mat_ambient[] = {0.7, 0.7, 0.7, 1.0};
GLfloat mat_diffuse[] = {0.6, 0.6, 0.6, 1.0};
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};//反射
GLfloat mat_shininess[] = {50.0};//光

glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);//环境光材质
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);//漫反射
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);//镜面反射
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);//光亮

//设置光源属性
GLfloat lightIntensity[] = {0.7, 0.7, 0.7, 1.0};
GLfloat lightPosition[] = {2.0, 6.0, 3.0, 0.0};
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);//设置光源0的位置
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightIntensity);//光源漫反射强度的RGBA值

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double winHt = 1.0; // half - height of th window
glOrtho(-winHt * 64 / 48.0, winHt * 64 / 48.0, -winHt, winHt, 0.1, 100.0);

glMatrixMode(GL_MODELVIEW);//设置照相机
glLoadIdentity();
gluLookAt(2.3, 1.3, 2, 0, 0.25, 0, 0.0, 1.0, 0.0);

//start draw
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glPushMatrix();
glTranslated(0.4, 0.4, 0.6);
glRotated(45, 0, 0, 1);
glScaled(0.08, 0.08, 0.08);
jack();//draw the jack
glPopMatrix();

glPushMatrix();
glTranslated(0.6, 0.38, 0.5);
glRotated(30, 0, 1, 0);
glutSolidTeapot(0.08);//draw the teapot
glPopMatrix();

glPushMatrix();
glTranslated(0.25, 0.42, 0.35);
glutSolidSphere(0.1, 15, 15);//draw the sphere
glPopMatrix();

glPushMatrix();
glTranslated(0.4, 0, 0.4);
table(0.6, 0.02, 0.02, 0.3);//draw the table
glPopMatrix();

wall(0.02); //wall #1: in xz-plane
glPushMatrix();
glRotated(90.0, 0.0, 0.0, 1.0);
wall(0.02);//wall #2: in xy-plane
glRotated(90.0, 1.0, 0.0, 0.0);
wall(0.02);
glPopMatrix();//wall #3: in yz-plane

glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("Shaded example-3D scene");
glutDisplayFunc(displaySolid);
myInit();
glutMainLoop();//进入消息循环

return 0;
}

5.6.4 使用SDL从文件中读取场景描述

SDLDraw

如标题所示,如果该类没有父类也没有任何子类,把析构函数还定义成虚的,确实没多大必要吧。对象的构建和析构完全是一个入栈和出栈的过程,也就是说肯定会从父类构造到子类,也肯定会从子类析构到父类,这些都是毋容置疑的。

那么把析构函数定义成virtual有个什么意义了。确实没有多大意义,至少对于一个非delete造成的析构。无论是析构一个堆栈对象还是全局对象,编译器肯定能在编译时就做出决策了。但是,假如有人一时兴起,new了一个子类对象并且将地址存储在基类指针中。那么,你该怎么删除这个对象了,只能delete父类指针了。

问题就出在这里了。你既然delete父类指针,假如是在编译层次决策的话,编译器只能帮你调用父类的析构函数了。但是,事实上你是一个子类对象,那么子类的析构函数没有调用,假如你在子类的析构函数做了些什么释放,结果就是那个释放永远不会执行,这样就造成内存泄漏或者资源重复申请之类的。

其实,上面的这种写法就是利用多态的性质,既然要利用多态,肯定得把析构函数定义成virtual的,那么调用哪个析构函数的决策就能到运行时候再决定。如果基类的析构函数定义成virtual的话,其所有子类的析构函数当然也是virtual的,那么我们删除基类指针的时候,就能通过多态机制决定该调用子类的析构函数,也就是从子类的析构函数开始往基类的析构函数调用释放整个对象。这样就不会造成任何资源泄漏了。

以下的代码展示了这样的一种情况,注意Class CA作为基类,就需要把析构函数定义成virtual的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>

class CA
{
public:
CA(int nS = 100): nSize(nS)
{
nA = new int[nSize];
printf("构造CA\n");
}
virtual ~CA()
{
delete [] nA;
printf("析构CA\n");
}

private:
int* nA;
int nSize;
};

class CB : public CA
{
public:
CB(int nS = 100): nSize(nS)
{
nB = new int[nSize];
printf("构造CB\n");
}
~CB()
{
delete [] nB;
printf("析构CB\n");
}

private:
int* nB;
int nSize;
};

int main()
{
CA* pCA = new CB();
delete pCA;
}

效果如图:

如果删掉CA析构函数前面的virtual,效果如图,

你也可以实验在主函数里面定义些栈变量的CA和CB的对象,无论构造函数是否是virtual的,都能够正确的析构。

写了下第三章部分作业和例子的代码,如下。

3.2节 sinc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <windows.h>
#include <math.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

const float PI = atan(1.0) * 4;
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;

//设置世界窗口
void SetWindow(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(left, right, bottom, top);
}

//设置视口
void SetViewport(GLint left, GLint right, GLint bottom, GLint top)
{
glViewport(left, bottom, right - left, top - bottom);
}

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//白色背景
glColor3f(0.0, 0.0, 1.0);
glLineWidth(2.0);
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);//使视图矩形栈有效
glLoadIdentity();

SetViewport(0, WINDOW_WIDTH / 2, 0, WINDOW_HEIGHT);//这个函数调用在main里面一直没效果
glBegin(GL_LINE_STRIP);
for (float x = -4.0; x <= 4.0; x += 0.1)
{
if (fabs(x) < 1e-8)
{
glVertex2f(0.0, 1.0);
}
else
{
glVertex2f(x, sin(PI * x) / (PI* x));
}
}
glEnd();

glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("The Famous Sinc Function");
glutDisplayFunc(myDisplay);
myInit();
SetWindow(-5.0, 5.0, -0.3, 1.0);
glutMainLoop();//进入消息循环

return 0;
}

3.2节 放大显示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <windows.h>
#include <math.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

const float PI = atan(1.0) * 4;
int nWidth = 640;
int nHeight = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;

//设置世界窗口
void SetWindow(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(left, right, bottom, top);
}

//设置视口
void SetViewport(GLint left, GLint right, GLint bottom, GLint top)
{
glViewport(left, bottom, right - left, top - bottom);
}

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//白色背景
glColor3f(0.0, 0.0, 1.0);
glLineWidth(2.0);
}

/////////////////////////////////////////////////////////////////
// Function: hexswirl()
// This draws a hexagon on the screen many times. Each new
// hexagon is slightly bigger than the previous one and rotated
// slightly so that a hexagon "swirl" is drawn.
/////////////////////////////////////////////////////////////////
void hexSwirl()
{
double angle; //the angle of rotation
double angleInc = 2*3.14159265/6.0; //the angle increment
double inc = 5.0 / 100; //the radius increment
double radius = 5.0 / 100.0; //the radius to be used

//glMatrixMode(GL_MODELVIEW);
//glLoadIdentity();
//clear the background

//draw the hexagon swirl
for (int j = 0; j <= 100; j++)
{
//the angle of rotation depends on which hexagon is
//being drawn.
angle = j* (3.14159265/180.0);

//draw one hexagon
glBegin (GL_LINE_STRIP);
for (int k=0; k <= 6; k++)
{
angle += angleInc;
glVertex2d(radius * cos(angle), radius *sin(angle));

}
glEnd();

//determine the radius of the next hexagon
radius += inc;
}

//swap buffers for a smooth change from one
//frame to another
glutSwapBuffers();
glFlush();
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
float cx = 0.3, cy = 0.2;
float H, W, aspect = 0.7;

int frame = 50;
for (int i = 0; i < frame; ++i)
{
glClear(GL_COLOR_BUFFER_BIT);
W *= 0.7;
H = W * aspect;
SetWindow(cx - W, cx + W, cy - H, cy + H);
hexSwirl();
}

glFlush();
}

void reShape(int nNewWidth, int nNewHeight)
{
nWidth = nNewWidth;
nHeight = nNewHeight;
SetViewport(0, nWidth, 0, nHeight);
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(nWidth, nHeight);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("hexSwirl");
glutDisplayFunc(myDisplay);
glutReshapeFunc(reShape);
myInit();
glutMainLoop();//进入消息循环

return 0;
}

3.2节 回旋的旋涡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <windows.h>
#include <math.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

const float PI = atan(1.0) * 4;
int nWidth = 640;
int nHeight = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int ROW = 8;
const int COLUMU = 6;

//设置世界窗口
void SetWindow(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(left, right, bottom, top);
}

//设置视口
void SetViewport(GLint left, GLint right, GLint bottom, GLint top)
{
glViewport(left, bottom, right - left, top - bottom);
}

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//白色背景
glColor3f(0.0, 0.0, 1.0);
glLineWidth(2.0);
}

/////////////////////////////////////////////////////////////////
// Function: hexswirl()
// This draws a hexagon on the screen many times. Each new
// hexagon is slightly bigger than the previous one and rotated
// slightly so that a hexagon "swirl" is drawn.
/////////////////////////////////////////////////////////////////
void hexSwirl()
{
double angle; //the angle of rotation
double angleInc = 2*3.14159265/6.0; //the angle increment
double inc = 5.0 / 100; //the radius increment
double radius = 5.0 / 100.0; //the radius to be used

//glMatrixMode(GL_MODELVIEW);
//glLoadIdentity();
//clear the background

//draw the hexagon swirl
for (int j = 0; j <= 100; j++)
{
//the angle of rotation depends on which hexagon is
//being drawn.
angle = j* (3.14159265/180.0);

//draw one hexagon
glBegin (GL_LINE_STRIP);
for (int k=0; k <= 6; k++)
{
angle += angleInc;
glVertex2d(radius * cos(angle), radius *sin(angle));

}
glEnd();

//determine the radius of the next hexagon
radius += inc;
}
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);

const int L = nWidth / ROW;

for (int i = 0; i < ROW; ++i)
{
for (int j = 0; j < COLUMU; ++j)
{
if ((i + j) % 2 == 0)
{
SetWindow(-0.6, 0.6, -0.6, 0.6);
}
else
{
SetWindow(-0.6, 0.6, 0.6, -0.6);
}
SetViewport(i * L, L + i * L, j * L, L + j * L);
hexSwirl();
//for (int k = 0; k <= 200000000; k++);
}
}

//swap buffers for a smooth change from one
//frame to another
glutSwapBuffers();
glFlush();
}

void reShape(int nNewWidth, int nNewHeight)
{
nWidth = nNewWidth;
nHeight = nNewHeight;
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(nWidth, nHeight);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("hexSwirl");
glutDisplayFunc(myDisplay);
glutReshapeFunc(reShape);
myInit();
glutMainLoop();//进入消息循环

return 0;
}

3.4节 5花环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int NUM = 55000;
const double PI = atan(1.0) * 4;

struct GLintPoint
{
GLint x;
GLint y;
};

class Point2
{
public:
float x, y;
void set(float dx, float dy) {x = dx; y = dy;}
void set(Point2 p) {x = p.x; y = p.y;}
Point2(float xx, float yy) {x = xx, y = yy;}
Point2() {x = y = 0;}
};
Point2 curpos, cp;

void moveTo(Point2 p)
{
cp.set(p);
}

void lineTo(Point2 p)
{
glBegin(GL_LINES);
glVertex2f(cp.x, cp.y);
glVertex2f(p.x, p.y);
glEnd();
glFlush();
cp.set(p);
}

void myInit()
{
glClearColor(1.0, 0.0, 0.0, 0.0);
glColor3f(0.0f, 1.0f, 0.0f);
glPointSize(4.0);//设置点的大小为4*4像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-WINDOW_WIDTH / 2, WINDOW_WIDTH / 2, -WINDOW_HEIGHT / 2, WINDOW_HEIGHT / 2);
}

void rosette(int N, float radius)
{
Point2* pointlist = new Point2[N];

GLfloat theta = (2.0 * PI) / N;
for (int c = 0; c < N; ++c)
{
pointlist``` stylus.set(radius * sin(c * theta), radius * cos(theta * c));
}

for (int i = 0; i < N; ++i)
{
for (int j = 0; j < N; ++j)
{
moveTo(pointlist[i]);
lineTo(pointlist[j]);
}
}
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);//清屏
glViewport(10, 10, 640, 480);
rosette(5, 200);
glFlush();//送往设备显示
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("花环");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}

3.4.3 阴阳符号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <algorithm>
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
using namespace std;
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

const double PI = atan(1.0) * 4;
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const double BIG_R = 100;
const double MID_R = 50;
const double SML_R = 10;

void myInit()
{
glClearColor(0.5, 0.5, 0.5, 0.0);
glColor3f(0.0, 0.0, 1.0);
glLineWidth(2.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);
}

void drawArc(double fX, double fY, double fR, double fBeg, double fEnd)
{
double fAdd = 0.0001;

fBeg = fBeg * PI / 180;
fEnd = fEnd * PI / 180;
if (fBeg > fEnd)
{
swap(fBeg, fEnd);
}

glBegin(GL_POLYGON);
while (fBeg < fEnd)
{
glVertex2f(fX + fR * cos(fBeg), fY + fR * sin(fBeg));
fBeg += fAdd;
}
glEnd();
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);

glColor3f(0.0, 0.0, 0.0);//黑
drawArc(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, BIG_R, 0, 180);
glColor3f(1.0, 1.0, 1.0);//白
drawArc(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, BIG_R, 180, 360);
glColor3f(0.0, 0.0, 0.0);//黑
drawArc(WINDOW_WIDTH / 2 - MID_R, WINDOW_HEIGHT / 2, MID_R, 0, 360);
glColor3f(1.0, 1.0, 1.0);//白
drawArc(WINDOW_WIDTH / 2 + MID_R, WINDOW_HEIGHT / 2, MID_R, 0, 360);
drawArc(WINDOW_WIDTH / 2 - MID_R, WINDOW_HEIGHT / 2, SML_R, 0, 360);
glColor3f(0.0, 0.0, 0.0);//黑
drawArc(WINDOW_WIDTH / 2 + MID_R, WINDOW_HEIGHT / 2, SML_R, 0, 360);

glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("阴阳图");
glutDisplayFunc(myDisplay);
myInit();
glutMainLoop();//进入消息循环

return 0;
}

3.4.4 Koch雪花

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <algorithm>
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
using namespace std;
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

const double PI = atan(1.0) * 4;
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int DEPTH = 8;

struct GLdoublePoint
{
GLdouble fX, fY;
};

GLdoublePoint operator + (const GLdoublePoint a, const GLdoublePoint b)
{
GLdoublePoint c;
c.fX = a.fX + b.fX;
c.fY = a.fY + b.fY;
return c;
}

GLdoublePoint operator - (const GLdoublePoint a, const GLdoublePoint b)
{
GLdoublePoint c;
c.fX = a.fX - b.fX;
c.fY = a.fY - b.fY;
return c;
}

GLdoublePoint operator * (const GLdoublePoint a, double fScale)
{
GLdoublePoint c;
c.fX = a.fX * fScale;
c.fY = a.fY * fScale;
return c;
}

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);
glColor3f(0.0, 0.0, 0.0);
glLineWidth(2.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);
}

void drawKoch(GLdoublePoint beg, GLdoublePoint end, int depth, bool left)
{
if (depth == 0)
{
glBegin(GL_LINES);
glVertex2f(beg.fX, beg.fY);
glVertex2f(end.fX, end.fY);
glEnd();
return;
}

GLdoublePoint pts[3];
GLdoublePoint vct = end - beg;
pts[0] = beg + (vct * (1.0 / 3.0));
pts[2] = beg + (vct * (2.0 / 3.0));
GLdoublePoint nvct;

if (left)
{
nvct.fX = -vct.fY;
nvct.fY = vct.fX;
}
else
{
nvct.fX = vct.fY;
nvct.fY = -vct.fX;
}

double fSize = sqrt(nvct.fX * nvct.fX + nvct.fY * nvct.fY);
double fLen = (fSize / 3) * (sqrt(3) / 2);
nvct.fX = nvct.fX / fSize * fLen;
nvct.fY = nvct.fY / fSize * fLen;
pts[1] = beg + (vct * 0.5);
pts[1] = pts[1] + nvct;

drawKoch(beg, pts[0], depth - 1, left);
drawKoch(pts[0], pts[1], depth - 1, left);
drawKoch(pts[1], pts[2], depth - 1, left);
drawKoch(pts[2], end, depth - 1, left);
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);

GLdoublePoint beg, end;
beg.fX = WINDOW_WIDTH / 3;
beg.fY = WINDOW_HEIGHT / 3 * 2;
end.fX = (WINDOW_WIDTH / 3) * 2;
end.fY = WINDOW_HEIGHT / 3 * 2;
drawKoch(beg, end, DEPTH, true);

beg.fX = WINDOW_WIDTH / 3;
beg.fY = WINDOW_HEIGHT / 3 * 2;
end.fX = WINDOW_WIDTH / 2;
end.fY = WINDOW_HEIGHT / 3 * 2 - (WINDOW_WIDTH / 3) * (sqrt(3) / 2);
drawKoch(beg, end, DEPTH, false);

beg.fX = WINDOW_WIDTH / 2;
beg.fY = WINDOW_HEIGHT / 3 * 2 - (WINDOW_WIDTH / 3) * (sqrt(3) / 2);
end.fX = WINDOW_WIDTH / 3 * 2;
end.fY = WINDOW_HEIGHT / 3 * 2;
drawKoch(beg, end, DEPTH, false);

glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("Koch雪花");
glutDisplayFunc(myDisplay);
myInit();
glutMainLoop();//进入消息循环

return 0;
}

3.5.3 心脏线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const double PI = atan(1.0) * 4;

void myInit()
{
glClearColor(1.0, 0.0, 0.0, 0.0);
glColor3f(0.0f, 1.0f, 0.0f);
glPointSize(4.0);
}

void myDisplay()
{
double fAdd = 0.01;
double K = 0.4;
double fX, fY, fValue;
glClear(GL_COLOR_BUFFER_BIT);//清屏

glBegin(GL_POINTS);
for (float fTheta = 0.0; fTheta <= 360; fTheta += fAdd)
{
double fAngle = fTheta / ( 2 * PI);
fValue = K * (1 + cos(fAngle));
fX = fValue * cos(fAngle);
fY = fValue * sin(fAngle);
glVertex2f(fX, fY);
}
glEnd();
glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("阿基米德螺线");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}

玫瑰曲线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const double PI = atan(1.0) * 4;

void myInit()
{
glClearColor(1.0, 0.0, 0.0, 0.0);
glColor3f(0.0f, 1.0f, 0.0f);
glPointSize(4.0);
}

void myDisplay()
{
double fAdd = 0.01;
double K = 1;
double fX, fY, fValue;
int N = 5;
glClear(GL_COLOR_BUFFER_BIT);//清屏

glBegin(GL_POINTS);
for (float fTheta = 0.0; fTheta <= 360; fTheta += fAdd)
{
double fAngle = fTheta / ( 2 * PI);
fValue = K * cos(N * fAngle);
fX = fValue * cos(fAngle);
fY = fValue * sin(fAngle);
glVertex2f(fX, fY);
}
glEnd();
glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("玫瑰曲线");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}

阿基米德曲线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const double PI = atan(1.0) * 4;

void myInit()
{
glClearColor(1.0, 0.0, 0.0, 0.0);
glColor3f(0.0f, 1.0f, 0.0f);
glPointSize(4.0);
}

void myDisplay()
{
double fAdd = 0.01;
double A = 0.01;
double fX, fY, fValue;
glClear(GL_COLOR_BUFFER_BIT);//清屏

glBegin(GL_POINTS);
for (float fTheta = 0.0; fTheta <= 360; fTheta += fAdd)
{
double fAngle = fTheta / ( 2 * PI);
fValue = A * fAngle;
fX = fValue * cos(fAngle);
fY = fValue * sin(fAngle);
glVertex2f(fX, fY);
}
glEnd();
glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("阿基米德螺线");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}

对数螺旋线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const double PI = atan(1.0) * 4;

void myInit()
{
glClearColor(1.0, 0.0, 0.0, 0.0);
glColor3f(0.0f, 1.0f, 0.0f);
glPointSize(2.0);
}

void myDisplay()
{
double fAdd = 0.01;
double K = 0.018;
double a = 0.07;
double fX, fY, fValue;
glClear(GL_COLOR_BUFFER_BIT);//清屏

glBegin(GL_POINTS);
for (float fTheta = 0.0; fTheta <= 360; fTheta += fAdd)
{
double fAngle = fTheta / ( 2 * PI);
fValue = K * exp(a * fAngle);
fX = fValue * cos(fAngle);
fY = fValue * sin(fAngle);
glVertex2f(fX, fY);
}
glEnd();
glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("对数螺旋线");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}

先讲下OpenGL的坐标系。OpenGL的坐标系和Windows窗口默认的坐标系是不同的。WindowsGDi的默认原点在左上角,X向右递增,Y向下递增,而OpenGL的原点在窗口的左下角,X向右递增,Y向上递增。

OpenGL里面所谓的窗口就是我们创建出来的窗口,也叫做屏幕窗口,我们可以在这整个窗口里面绘图。而视口则是窗口的一个子区域,通过设置视口就可以把绘图区域限定为窗口的一个子区域,而这个子区域的别名就叫做视口。这个函数是glViewport。该函数原型为
void glViewport(GLint x,
GLint y,
GLsizei width,
GLsizei height)

第一个参数和第二个参数是左下角的坐标,第三个参数和第四个参数则分别是视口的宽度和高度。记住一点,OpenGL用的是笛卡尔坐标系,而我一直习惯了WindowsGDI的坐标系设置。

现在来讲解最关键的东西—-世界窗口(裁剪区)。

这个东西理解了,我们就可以通过一些设置做出一些漂亮的图形裁剪啊之类的,总之能达到一些很爽的效果。以计算机图形学OpenGL版3.2节的例子来说明,世界窗口即是能容纳sinc(x) = sin(PIx) / (PI x)的定义域和值域的二维矩阵。比如说,这个例子里面绘画函数的时候x的范围是[-4.0,4.0],该函数最大值不会超过1.0,在该范围内最小值也会大于-0.3,那么我们可以设置世界窗口为[-4.0,4.0,-0.3,1.0],四个值分别是描述了现实世界中的一个与坐标轴平行的矩形。如果把世界窗口设置为[-5.0,5.0,-0.3,1.0],该函数的显示效果可能会更好一点,因为可以看到边界外面的部分。

现在很明显的看到,世界窗口和视口大小基本上是不一致的,那么我们要显示的图形肯定会被拉伸。这个拉伸也是很简单的按照比例尺计算出来的。可以很形象的想象下,把一个人放小显示在一个窗口里面,或者把一只蚂蚁放大显示在一个窗口里面。

附3.2节sinc源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <windows.h>
#include <math.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

const float PI = atan(1.0) * 4;
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;

//设置世界窗口
void SetWindow(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(left, right, bottom, top);
}

//设置视口
void SetViewport(GLint left, GLint right, GLint bottom, GLint top)
{
glViewport(left, bottom, right - left, top - bottom);
}

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//白色背景
glColor3f(0.0, 0.0, 1.0);
glLineWidth(2.0);
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);//使视图矩形栈有效
glLoadIdentity();

SetViewport(0, WINDOW_WIDTH / 2, 0, WINDOW_HEIGHT);//这个函数调用在main里面一直没效果
glBegin(GL_LINE_STRIP);
for (float x = -4.0; x <= 4.0; x += 0.1)
{
if (fabs(x) < 1e-8)
{
glVertex2f(0.0, 1.0);
}
else
{
glVertex2f(x, sin(PI * x) / (PI* x));
}
}
glEnd();

glFlush();
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("The Famous Sinc Function");
glutDisplayFunc(myDisplay);
myInit();
SetWindow(-5.0, 5.0, -0.3, 1.0);
glutMainLoop();//进入消息循环

return 0;
}

显示效果如图,

由于我设置了视口为左半窗口,可以明显的看到,函数曲线挤压在左边了。注意,在主函数里面设置视口一直没有效果,我也不知道为什么。

最近在用这本书学习计算机图形学,发现这本书在网上基本找不到配套的代码,找到的也是乱七八糟的,很不爽的那种,或者根本是语言初学者对着敲的。故打算把自己写的每个例子贡献出来。

散乱点图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int NUM = 1000;

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为亮白
glColor3f(0.0f, 0.0f, 0.0f);//设置绘图颜色为黑色
glPointSize(4.0);//设置点的大小为4*4像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);//以上三句话合起伙是设置窗口的坐标系
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);//清屏
glBegin(GL_POINTS);
for (int i = 0; i < NUM; ++i)
{
glVertex2i(rand() % WINDOW_WIDTH, rand() % WINDOW_HEIGHT);
}
glEnd();
glFlush();//送往设备显示
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("散乱点图");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
srand(time(NULL));
glutMainLoop();//进入消息循环

return 0;
}

Sierpinski垫片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int NUM = 55000;

struct GLintPoint
{
GLint x;
GLint y;
};
GLintPoint pts[3] = {{10, 10}, {600, 10}, {300, 600}};

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为亮白
glColor3f(0.0f, 0.0f, 0.0f);//设置绘图颜色为黑色
glPointSize(4.0);//设置点的大小为4*4像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);//以上三句话合起伙是设置窗口的坐标系
}

void drawDot(GLintPoint pt)
{
glBegin(GL_POINTS);
glVertex2i(pt.x, pt.y);
glEnd();
}

void sierpinskiRender()
{
glClear(GL_COLOR_BUFFER_BIT);//清屏
int nIndex = rand() % 3;
GLintPoint pt = pts[nIndex];

drawDot(pt);
for (int i = 0; i < NUM; ++i)
{
nIndex = rand() % 3;
pt.x = (pt.x + pts[nIndex].x) / 2;
pt.y = (pt.y + pts[nIndex].y) / 2;
drawDot(pt);
}
glFlush();//送往设备显示
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("Sierpinski垫片");
glutDisplayFunc(sierpinskiRender);//注册重绘回调函数
myInit();
srand(time(NULL));
glutMainLoop();//进入消息循环

return 0;
}

绘制函数曲线:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const double PI = atan(1.0) * 4.0;
GLdouble A, B, C, D;

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为亮白
glColor3f(0.0f, 0.0f, 0.0f);//设置绘图颜色为黑色
glPointSize(2.0);//设置点的大小为2*2像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);//以上三句话合起伙是设置窗口的坐标系
A = WINDOW_WIDTH / 4.0;
B = 0.0;
C = D = WINDOW_HEIGHT / 2.0;
}

void myDisplay()
{
GLdouble fAdd = 0.001;

glClear(GL_COLOR_BUFFER_BIT);//清屏
glBegin(GL_POINTS);
for (GLdouble x = 0.0; x < 4.0; x += fAdd)
{
GLdouble fValue = exp(-x) * cos(2 * PI * x);
glVertex2d(A * x + B, C * fValue + D);
}
glEnd();
glFlush();//送往设备显示
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("Dot Plot of a Function");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}

画矩形:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int NUM = 8;
const int BOARD_WIDTH = WINDOW_WIDTH / NUM;
const int BOARD_HEIGHT = WINDOW_HEIGHT / NUM;
const GLfloat r1 = 0.0, g1 = 0.0, b1 = 0.0;
const GLfloat r2 = 1.0, g2 = 1.0, b2 = 1.0;

struct GLintPoint
{
GLint x;
GLint y;
};

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为亮白
glColor3f(0.0f, 0.0f, 0.0f);//设置绘图颜色为黑色
glPointSize(2.0);//设置点的大小为2*2像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);//以上三句话合起伙是设置窗口的坐标系
}

//根据中心,高和宽绘画矩形
void drawRectangleCenter(GLintPoint center, double fWidth, double fHeight)
{
glRectf(center.x - fWidth / 2.0, center.y - fHeight / 2.0,
center.x + fWidth / 2.0, center.y + fHeight / 2.0);
}

//根据左上角,高和宽高比绘画矩形
void drawRectangleCornersize(GLintPoint topleft, double fHeight, double fScale)
{
glRectf(topleft.x, topleft.y, topleft.x + fHeight * fScale, topleft.y + fHeight);
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);//清屏
GLintPoint center, topleft;

for (int i = 0; i < NUM; ++i)
{
for (int j = 0; j < NUM; ++j)
{
if ((i + j) % 2 == 0)
{
glColor3f(r1, g1, b1);
center.x = i * BOARD_WIDTH + BOARD_WIDTH / 2.0;
center.y = j * BOARD_HEIGHT + BOARD_HEIGHT / 2.0;
drawRectangleCenter(center, BOARD_WIDTH, BOARD_HEIGHT);
}
else
{
glColor3f(r2, g2, b2);
topleft.x = i * BOARD_WIDTH;
topleft.y = j * BOARD_HEIGHT;
drawRectangleCornersize(topleft, BOARD_HEIGHT, BOARD_HEIGHT * 1.0 / BOARD_WIDTH);
}
}
}
glFlush();//送往设备显示
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("画矩形");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}

画棋盘:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <math.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int NUM = 8;
const int BOARD_WIDTH = WINDOW_WIDTH / NUM;
const int BOARD_HEIGHT = WINDOW_HEIGHT / NUM;
const GLfloat r1 = 0.0, g1 = 0.0, b1 = 0.0;
const GLfloat r2 = 1.0, g2 = 1.0, b2 = 1.0;

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为亮白
glColor3f(0.0f, 0.0f, 0.0f);//设置绘图颜色为黑色
glPointSize(2.0);//设置点的大小为2*2像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);//以上三句话合起伙是设置窗口的坐标系
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);//清屏
for (int i = 0; i < NUM; ++i)
{
for (int j = 0; j < NUM; ++j)
{
if ((i + j) % 2 == 0)
{
glColor3f(r1, g1, b1);
}
else
{
glColor3f(r2, g2, b2);
}
glRecti(i * BOARD_WIDTH, j * BOARD_HEIGHT,
i * BOARD_WIDTH + BOARD_WIDTH, j * BOARD_HEIGHT + BOARD_HEIGHT);
}
}
glFlush();//送往设备显示
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("棋盘");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}

根据不同长宽比画矩形:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 400;
const int WINDOW_HEIGHT = 400;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int MAX = 100000;
double R = 1.7;

struct GLintPoint
{
GLint x;
GLint y;
};

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为亮白
glColor3f(0.0f, 0.0f, 0.0f);//设置绘图颜色为黑色
glPointSize(2.0);//设置点的大小为2*2像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);//以上三句话合起伙是设置窗口的坐标系
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);//清屏

double fWidth, fHeight;
if (R > 1.0)
{
fWidth = WINDOW_WIDTH;
fHeight = fWidth / R;
}
else
{
fHeight = WINDOW_HEIGHT;
fWidth = fHeight * R;

}
glRectf(0.0, 0.0, fWidth, fHeight);
R = (rand() % MAX) * 1.0 / (MAX / 10.0);

glFlush();//送往设备显示
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("不同长宽比");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
srand(time(NULL));
glutMainLoop();//进入消息循环

return 0;
}

鼠标和键盘:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int NUM = 1000;

struct GLintPoint
{
GLint x, y;
};
GLintPoint corner[2];
bool bSelect = false;

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为亮白
glColor3f(0.0f, 0.0f, 0.0f);//设置绘图颜色为黑色
glPointSize(4.0);//设置点的大小为4*4像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);//以上三句话合起伙是设置窗口的坐标系
glLineWidth(4.0);
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);//清屏

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

if (bSelect)
{
glBegin(GL_QUADS);
glVertex2i(corner[0].x, corner[0].y);
glVertex2i(corner[0].x, corner[1].y);
glVertex2i(corner[1].x, corner[1].y);
glVertex2i(corner[1].x, corner[0].y);
glEnd();
}
glutSwapBuffers();
}

void myMouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON state == GLUT_DOWN)
{
corner[0].x = x;
corner[0].y = WINDOW_HEIGHT - y;
bSelect = true;
}
glutPostRedisplay();
}

//鼠标在窗口上面移动并且没有按钮按下
void myPassiveMotion(int x, int y)
{
corner[1].x = x;
corner[1].y = WINDOW_HEIGHT - y;
glutPostRedisplay();
}

void myKeyboard(unsigned char key, int mouseX, int mouseY)
{
switch (key)
{
case 'P':
case 'p':
glBegin(GL_POINTS);
glVertex2i(mouseX, WINDOW_HEIGHT - mouseY);
glEnd();
glutSwapBuffers();
break;

case 'Q':
case 'q':
exit(-1);
break;

default:
break;
}
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("Rubber Rect Demo");
glutDisplayFunc(myDisplay);//注册重绘回调函数
glutMouseFunc(myMouse);
glutPassiveMotionFunc(myPassiveMotion);
glutKeyboardFunc(myKeyboard);
myInit();
glutMainLoop();//进入消息循环

return 0;
}

菜单:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;

enum COLOR
{
RED, GREEN, BLUE, WHITE
};
float angle = 0.0;
float red = 1.0, blue = 1.0, green = 1.0;

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清屏
glLoadIdentity();

glRotatef(angle, 0.0, 1.0, 0.0);
glColor3f(red, green, blue);
glBegin(GL_TRIANGLES);//下面的画点坐标为什么是这么设置的了?
glVertex3f(-0.5, -0.5, 1.0);
glVertex3f(0.5, 0.0, 0.0);
glVertex3f(0.0, 0.5, 2.0);
glEnd();
angle++;
glutSwapBuffers();
}

void myMenuEvents(int option)
{
switch (option)
{
case RED: red = 1.0; green = blue = 0.0; break;
case GREEN: green = 1.0; red = blue = 0.0; break;
case BLUE: blue = 1.0; red = green = 0.0; break;
case WHITE: red = green = blue = 1.0; break;
}
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("Menu Test");
glutDisplayFunc(myDisplay);//注册重绘回调函数
glutIdleFunc(myDisplay);
glutCreateMenu(myMenuEvents);
glutAddMenuEntry("Red", RED);
glutAddMenuEntry("Blue", BLUE);
glutAddMenuEntry("Green", GREEN);
glutAddMenuEntry("Black", WHITE);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutMainLoop();//进入消息循环

return 0;
}

姜饼人:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <math.h>
#include <stdlib.h>
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //设置连接器选项

const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int WINDOW_POS_X = 300;
const int WINDOW_POS_Y = 150;
const int M = 40;
const int L = 3;
const int TIMES = 1000000;

struct GLintPoint
{
GLint x, y;
};
GLintPoint p, q;

void myInit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为亮白
glColor3f(0.0f, 0.0f, 0.0f);//设置绘图颜色为黑色
glPointSize(2.0);//设置点的大小为2*2像素
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);//以上三句话合起伙是设置窗口的坐标系
p.x = 121;
p.y = 115;
}

void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);//清屏
glBegin(GL_POINTS);
for (int i = 0; i < TIMES; ++i)
{
q.x = M * (1 + 2 * L) - p.y + abs(p.x - L * M);
q.y = p.x;
p = q;
glVertex2f(p.x, p.y);
}
glEnd();
glFlush();//送往设备显示
}

int main(int argc, char** argv)
{
glutInit(argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//设置显示模式
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y);
glutCreateWindow("姜饼人");
glutDisplayFunc(myDisplay);//注册重绘回调函数
myInit();
glutMainLoop();//进入消息循环

return 0;
}