write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
OpenGL系列文章都已經(jīng)寫到3D部分了,但是感覺2D的例子還是少了點(diǎn),特意弄個(gè)外篇,引入一些稍微復(fù)雜的例子,這些例子不適合作為概念介紹時(shí)寫入此系列文章,只好獨(dú)立成篇,自己完成一個(gè)就發(fā)布一個(gè)吧。
此次完成的是一個(gè)七巧板圖形的繪制,繪制時(shí)參考了網(wǎng)上的七巧板程序的顏色( 此處 ),因?yàn)槲覂H僅是為了加深對(duì)OpenGL的學(xué)習(xí),沒有添加具體的真實(shí)游戲代碼,僅僅只有OpenGL圖形繪制的演示。
另外,因?yàn)樽鳛閷W(xué)習(xí)OpenGL例子之用,自然要使用OpenGL來繪制啦,雖然這么簡單的圖形根本用不著OpenGL,別說我用大炮打蒼蠅,用牛刀殺雞-_-!因?yàn)槭菍W(xué)習(xí)OpenGL嘛,所以在2D坐標(biāo)系中還是使用了OpenGL的坐標(biāo)系,并且,為了追求顯示的完美(主要是旋轉(zhuǎn)的),我選取了所有圖形的重心作為每個(gè)圖形的坐標(biāo)系的原點(diǎn),這樣旋轉(zhuǎn)時(shí)的中心就是每個(gè)圖形的中心,這樣比使用rgnbox獲取邊框然后取中心旋轉(zhuǎn)效果要好,具體的旋轉(zhuǎn)效果需要自己添加代碼去嘗試了,Rotate和RotateTo的實(shí)現(xiàn)已經(jīng)有了。添加到繪制部分即可。
下面簡單介紹一下所有源代碼。
所有的七巧板圖形全部繼承自CGLShape,此類實(shí)現(xiàn)如下
// 此類實(shí)現(xiàn)移動(dòng),旋轉(zhuǎn),
class CGLShape
{
public :
CGLShape( void )
{
mfPosX = 0.0;
mfPosY = 0.0;
memset(mfvColor, 0, sizeof (mfvColor));
mfDegree = 0.0;
mfSize = 1.0;
}
virtual ~CGLShape( void ) { }
void SetSize(GLfloat afSize) { mfSize = afSize; }
void SetColor(GLfloat afRed, GLfloat afGreen, GLfloat afBlue, GLfloat afAlpha = 1.0)
{
mfvColor[0] = afRed;
mfvColor[1] = afGreen;
mfvColor[2] = afBlue;
mfvColor[3] = afAlpha;
}
// 相對(duì)偏移(外部坐標(biāo)系長度)
void Move( int aiPosX, int aiPosY)
{
mfPosX += 2.0f * aiPosX/( float )WIDTH;
mfPosY += -(2.0f * aiPosY/( float )HEIGHT);
}
// 移動(dòng)到的位置(Windows坐標(biāo)系點(diǎn))
void MoveTo( int aiPosX, int aiPosY)
{
mfPosX = (2.0f * aiPosX - ( float )WIDTH) / ( float )WIDTH;
mfPosY = -(2.0f * aiPosY - ( float )HEIGHT) / ( float )HEIGHT;
}
// 旋轉(zhuǎn),以逆時(shí)針為正方向
void Rotate(GLfloat aiDegree) { mfDegree += aiDegree; }
// 旋轉(zhuǎn)到
void RotateTo(GLfloat aiDegree) { mfDegree = aiDegree; }
// OpenGL坐標(biāo)系
void SetPos(GLfloat afPosX, GLfloat afPosY) { mfPosX = afPosX; mfPosY = afPosY; }
void GetPos(GLfloat& afPosX, GLfloat& afPosY) { afPosX = mfPosX; afPosY = mfPosY; }
void Draw()
{
glLoadIdentity();
glColor4fv(mfvColor);
glPushMatrix();
glTranslatef(mfPosX, mfPosY, 0.0);
glRotatef(mfDegree, 0.0, 0.0, 1.0);
DrawImp();
glPopMatrix();
}
protected :
// 重繪時(shí)需要調(diào)用的函數(shù),由各子類實(shí)現(xiàn)
virtual void DrawImp() = 0;
// 位置信息,沒有用POINT是為了將來方便移植
GLfloat mfPosX;
GLfloat mfPosY;
// 顏色
GLfloat mfvColor[4];
// 旋轉(zhuǎn)度數(shù)
GLfloat mfDegree;
// 大小
GLfloat mfSize;
};
大小,顏色,旋轉(zhuǎn)角度都以成員變量的方式保存在CGLShape中,并且在Draw函數(shù)中已經(jīng)實(shí)際的完成了使用,具體的DrawImp交由子類實(shí)現(xiàn),完成具體圖形的繪制。(此處使用template模式)具體的各個(gè)實(shí)現(xiàn)按照七巧板的要求如下:
三角形:
class CGLTriangle : public CGLShape
{
public :
CGLTriangle() { }
virtual ~CGLTriangle() { }
// 重繪時(shí)需要調(diào)用的函數(shù),由各子類實(shí)現(xiàn)
virtual void DrawImp()
{
static GLfloat fvTop[3] = { 0.0, (1.0/3.0), 0.0};
static GLfloat fvLeftBottom[3] = { -0.5, -(1.0/6.0), 0.0};
static GLfloat fvRightBottom[3] = { 0.5, -(1.0/6.0), 0.0};
GLfloat fvSizeTop[3] = {fvTop[0], fvTop[1] * mfSize, fvTop[2]};
GLfloat fvSizeLeftBottom[3] = {fvLeftBottom[0] * mfSize, fvLeftBottom[1] * mfSize, fvLeftBottom[2]};
GLfloat fvSizeRightBottom[3] = {fvRightBottom[0] * mfSize, fvRightBottom[1] * mfSize, fvRightBottom[2]};
glBegin(GL_TRIANGLES);
glVertex3fv(fvSizeTop);
glVertex3fv(fvSizeLeftBottom);
glVertex3fv(fvSizeRightBottom);
glEnd();
}
};
實(shí)際上DrawImp僅僅關(guān)心繪制一個(gè)三角形而已,這里因?yàn)槠渌麍D形只有一個(gè),所以不關(guān)心大小,而三角形在七巧板中有多個(gè),所以將大小的計(jì)算放在三角形中,獨(dú)立計(jì)算大小,事實(shí)上更通用的辦法是在CShape的Draw函數(shù)中用glScale實(shí)現(xiàn)。三角形的3個(gè)頂點(diǎn)的坐標(biāo)相對(duì)來說還比較容易計(jì)算。
矩形:
class CGLRectangle: public CGLShape
{
public :
CGLRectangle() { }
virtual ~CGLRectangle() { }
// 重繪時(shí)需要調(diào)用的函數(shù),由各子類實(shí)現(xiàn)
virtual void DrawImp()
{
static GLfloat fLength = sqrt(2.0)/4.0;
static GLfloat fLengthHalf = fLength/2.0;
static GLfloat fvLeftTop[3] = { -fLengthHalf, fLengthHalf, 0.0};
static GLfloat fvRightTop[3] = { fLengthHalf, fLengthHalf, 0.0};
static GLfloat fvLeftBottom[3] = { -fLengthHalf, -fLengthHalf, 0.0};
static GLfloat fvRightBottom[3] = { fLengthHalf, -fLengthHalf, 0.0};
// TODO:因?yàn)槠呓前宕死蟹叫沃挥幸粋€(gè)不需要考慮大小,暫時(shí)不計(jì)算大小
glColor4fv(mfvColor);
glPushMatrix();
glBegin(GL_QUADS);
glVertex3fv(fvLeftTop);
glVertex3fv(fvRightTop);
glVertex3fv(fvRightBottom);
glVertex3fv(fvLeftBottom);
glEnd();
glPopMatrix();
}
};
很簡單,不多說了,DrawImp實(shí)現(xiàn)一個(gè)矩形的繪制而已。四個(gè)頂點(diǎn)的坐標(biāo)時(shí)最容易計(jì)算的。
平行四邊形:
class CGLParallelogram: public CGLShape
{
public :
CGLParallelogram() { }
virtual ~CGLParallelogram() { }
// 重繪時(shí)需要調(diào)用的函數(shù),由各子類實(shí)現(xiàn)
virtual void DrawImp()
{
static GLfloat fRightBottomX = 0.375;
static GLfloat fHeight = 0.25;
static GLfloat fvLeftTop[3] = { -fRightBottomX, fHeight/2.0, 0.0};
static GLfloat fvRightTop[3] = { 0.5-fRightBottomX, fHeight/2, 0.0};
static GLfloat fvLeftBottom[3] = { fRightBottomX-0.5, -fHeight/2.0, 0.0};
static GLfloat fvRightBottom[3] = { fRightBottomX, -fHeight/2.0, 0.0};
// TODO:因?yàn)槠呓前宕死兴倪呅沃挥幸粋€(gè)不需要考慮大小,暫時(shí)不計(jì)算大小
glColor4fv(mfvColor);
glPushMatrix();
glRotatef(mfDegree, 0.0, 0.0, 1.0);
glBegin(GL_QUADS);
glVertex3fv(fvLeftTop);
glVertex3fv(fvRightTop);
glVertex3fv(fvRightBottom);
glVertex3fv(fvLeftBottom);
glEnd();
glPopMatrix();
}
};
繪制是同樣簡單,但是在以平行四邊形的重心為原點(diǎn)的坐標(biāo)系中,計(jì)算平時(shí)四邊形的4個(gè)頂點(diǎn)的坐標(biāo)需要一定的計(jì)算量,上述那幾個(gè)數(shù)值可不是隨便掰出來的啊。
將上述圖形組合在一起
CGLTriangle gTriBTop; // 上方的大三角形
CGLTriangle gTriBRight; // 右邊的大三角形
CGLTriangle gTriSLeft; // 左上方的小上角形
CGLTriangle gTriSMid; // 中間的小三角形
CGLTriangle gTriMLeft; // 左下方的中三角形
CGLRectangle gRectangle; // 唯一的正方形
CGLParallelogram gParal; // 唯一的平行四邊形
//OpenGL初始化開始
void SceneInit( int w, int h)
{
GLenum err = glewInit();
if (err != GLEW_OK)
{
MessageBox(NULL, _T( "Error" ), _T( "Glew init failed." ), MB_OK);
exit(-1);
}
gTriBTop.SetColor(1.0, 0.0, 0.0); // 上方的大三角形,紅色,
gTriBTop.SetPos(0.0, 1.0/3.0);
gTriBTop.RotateTo(180.0);
gTriBRight.SetColor(0.0, 0.0, 1.0); // 右邊的大三角形,藍(lán)色
gTriBRight.SetPos(1.0/3.0, 0.0);
gTriBRight.RotateTo(90.0);
gTriSLeft.SetColor(1.0, 1.0, 0.0); // 左上方的小上角形,黃色
gTriSLeft.SetSize(0.5); // 小三角形只有大的一半大
gTriSLeft.SetPos(-(5.0/12.0), 0.25);
gTriSLeft.RotateTo(-90.0);
gRectangle.SetColor(1.0, 175.0/255.0, 175.0/255.0); // 唯一的正方形,不知道什么顏色
gRectangle.SetPos(-0.25, 0.0);
gRectangle.RotateTo(45.0);
gTriSMid.SetColor(0.0, 1.0, 0.0); // 中間的小三角形,綠色
gTriSMid.SetSize(0.5); // 小三角形只有大的一半大
gTriSMid.SetPos(0.0, -1.0/6.0);
gTriMLeft.SetColor(0.0, 1.0, 1.0); // 左下方的中三角形
gTriMLeft.SetSize( sqrt(2.0) / 2.0 ); // 以底邊計(jì)算,2分之根號(hào)2倍
gTriMLeft.SetPos(-0.5 + 1.0/6.0, -0.5 + 1.0/6.0 );
gTriMLeft.RotateTo(135.0);
gParal.SetColor(1.0, 0.0, 1.0); // 唯一的平行四邊形,紫色
gParal.SetPos(0.125, -0.375);
}
void ReShape( unsigned auWidth, unsigned auHeight)
{
glViewport(0, 0, auWidth, auHeight);
}
//這里進(jìn)行所有的繪圖工作
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT); // 清空顏色緩沖區(qū)
gTriBTop.Draw();
gTriBRight.Draw();
gTriSLeft.Draw();
gRectangle.Draw();
gTriSMid.Draw();
gTriMLeft.Draw();
gParal.Draw();
glFlush();
}
上述代碼中程序邏輯非常簡單,無非就是設(shè)定好每個(gè)圖形的大小,位置,旋轉(zhuǎn)角度(三角形需要)等,每個(gè)圖形的繪制都是以其重心為原點(diǎn),所以計(jì)算的實(shí)際是七巧板每個(gè)圖形的重心的位置,有一定的計(jì)算量,上述每個(gè)數(shù)值都是精確計(jì)算出來的,所以最后的圖形才能完美,實(shí)際上也可以通過移動(dòng)所有圖形到一起,然后記錄下每個(gè)值的方式來完成,但是此方式可能沒有完整的數(shù)學(xué)描述精確,效果也就沒有那么完美了。顯示的截圖如下:
最后演示一下每個(gè)圖形的旋轉(zhuǎn)效果:
只需要將代碼改成如下形式即可:
//這里進(jìn)行所有的繪圖工作
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT); // 清空顏色緩沖區(qū)
gTriBTop.Draw();
gTriBRight.Draw();
gTriSLeft.Draw();
gRectangle.Draw();
gTriSMid.Draw();
gTriMLeft.Draw();
gParal.Draw();
gTriBTop.Rotate(1.0);
gTriBRight.Rotate(1.0);
gTriSLeft.Rotate(1.0);
gRectangle.Rotate(1.0);
gTriSMid.Rotate(1.0);
gTriMLeft.Rotate(1.0);
gParal.Rotate(1.0);
glFlush();
}
見視頻:
此文演示的完整代碼在博客代碼的JTTangram目錄。有興趣的可以將整個(gè)七巧板游戲完成然后再告訴我。最后,開始實(shí)際的完成稍微復(fù)雜點(diǎn)的圖形的繪制時(shí)就會(huì)發(fā)現(xiàn),就會(huì)發(fā)現(xiàn)技術(shù)的學(xué)習(xí)都是比較簡單的,但是,實(shí)際的使用全靠數(shù)學(xué)撐著,此例算是比較簡單了,但是平面幾何,三角函數(shù)等東西不懂一點(diǎn)根本無法計(jì)算各個(gè)圖形的位置-_-!數(shù)學(xué)還是得學(xué)好啊。。。。。。。。。。。
本OpenGL系列其他文章
1. 《 Win32 OpenGL 編程(1)Win32下的OpenGL編程必須步驟 》
2. 《 Win32 OpenGL編程(2) 尋找缺失的OpenGL函數(shù) 》
3. 《 Win32 OpenGL編程(3) 基本圖元(點(diǎn),直線,多邊形)的繪制 》
4. 《 Win32 OpenGL編程(4) 2D圖形基礎(chǔ)(顏色及坐標(biāo)體系進(jìn)階知識(shí)) 》
5. 《 Win32 OpenGL編程(5)頂點(diǎn)數(shù)組詳細(xì)介紹 》
6.《 Win32 OpenGL編程(6) 踏入3D世界 》
完整源代碼獲取說明
由于篇幅限制,本文一般僅貼出代碼的主要關(guān)心的部分,代碼帶工程(或者makefile)完整版(如果有的話)都能用Mercurial在Google Code中下載。文章以博文發(fā)表的日期分目錄存放,請直接使用Mercurial克隆下庫:
https://blog-sample-code.jtianling.googlecode.com/hg/
Mercurial使用方法見《 分布式的,新一代版本控制系統(tǒng)Mercurial的介紹及簡要入門 》
要是僅僅想瀏覽全部代碼也可以直接到google code上去看,在下面的地址:
http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code
原創(chuàng)文章作者保留版權(quán) 轉(zhuǎn)載請注明原作者 并給出鏈接
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
