OpenGL學習進程(8)第六課:點、邊和圖形(三)繪制圖形


    本節是OpenGL學習的第六個課時,下面介紹OpenGL圖形的相關知識:

 

    (1)多邊形的概念:

    多邊形是由多條線段首尾相連而形成的閉合區域。OpenGL規定,一個多邊形必須是一個“凸多邊形”。通過點、直線和多邊形,就可以組合成各種幾何圖形。一段弧可以看成是是很多短的直線段相連,這些直線段足夠短,以至於其長度小於一個像素的寬度。通過位於不同平面的相連的小多邊形,還可以組成一個“曲面”。

    什么是凸邊形:

    凸邊形:多邊形內任意兩點所確定的線段都在多邊形內,由此也可以推導出,凸多邊形不能是空心的。

    凸邊形與凹邊形:

    凸多邊形又可稱為平面多邊形,是多邊形中的一種,與凹多邊形相對。凸多邊形的內角均小於180°,而凹邊形內角有的可以大於180°。

    驗證方法:

    將圖形的任意一邊無限延長,如果延長線與圖形有交點就是凹邊形,反之就是凸邊形。

    多邊形是OpenGL中繪制曲面的基礎。

 

    (2)如何繪制多邊形:

    1)三維坐標系下多邊形的兩面及其繪制方式:

      從三維的角度來看,一個多邊形具有兩個面,每一個面都可以設置不同的繪制方式:

繪制方式:
1.填充
2.只繪制邊緣輪廓線
3.只繪制頂點
(“填充”是默認的方式)

      void glPolygonMode(GLenum face,GLenum mode)函數用於控制多邊形的顯示方式:

face參數確定顯示模式將適用於物體的哪些部分,控制多邊形的正面和背面的繪圖模式:
1.GL_FRONT 表示顯示模式將適用於物體的前向面(也就是物體能看到的面)
2.GL_BACK  表示顯示模式將適用於物體的后向面(也就是物體上不能看到的面)
3.GL_FRONT_AND_BACK  表示顯示模式將適用於物體的所有面
mode參數確定選中的物體的面以何種方式顯示(顯示模式):
1.GL_POINT 表示只顯示頂點,多邊形用點顯示
2.GL_LINE  表示顯示線段,多邊形用輪廓顯示
3.GL_FILL  表示顯示面,多邊形采用填充形式
可以為兩個面分別設置不同的顯示方式:
glPolygonMode(GL_FRONT, GL_FILL);            //設置正面為填充方式
glPolygonMode(GL_BACK, GL_LINE);             //設置反面為邊緣繪制方式
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);  //設置兩面均為頂點繪制方式

    2)反轉(切換正反面):

      一般約定:頂點以逆時針順序出現在屏幕上的面為正面,另一個面即成為反面。可以使用glFrontFace函數來調換正面與反面的概念。

      void glFrontFace(GLenum mode)作用是控制多邊形的正面是如何決定的:

mode參數的值:
1.GL_CCW 表示窗口坐標上投影多邊形的頂點順序為逆時針方向的表面為正面。(CounterClockWise)
2.GL_CW  表示頂點順序為順時針方向的表面為正面。(ClockWise)
頂點的方向又稱為環繞。
(在默認情況下,mode是GL_CCW) 

      如果一個虛構的對象的頂點是按照多邊形內部順時針的方向進行繪制的,那么可以稱這個多邊形基於窗口坐標的投影是順時針的。反之,則為逆時針。glFrontFace就是用來指定多邊形在窗口坐標中的方向是逆時針還是順時針的。

    3)剔除多邊形表面:

      在一個全部由不透明封閉表面組成的場景中,背面多邊形是永遠看不見的。剔除這些不可見的多邊形對於加速圖形的渲染有很大的益處。

      OpenGL中使用glCullFace()來進行剔除操作:

剔除操作的步驟:
1.glEnable(GL_CULL_FACE):啟動剔除功能;(glDisable(GL_CULL_FACE)關閉剔除功能)
2.使用glCullFace()來進行剔除;

      void glCullFace(GLenum mode)指明前面或后面的多邊形是否要剔除:

mode參數的值:
1.GL_FRONT   表示剔除正面
2.GL_BACK     剔除反面
3.GL_FRONT_AND_BACK  剔除正反兩面的多邊形

    4)鏤空多邊形:

      直線可以被畫成虛線,而多邊形則可以進行鏤空。

      OpenGL中使用glPolygonStipple()函數設置鏤空的樣式:

鏤空操作的步驟:
1.使用glEnable(GL_POLYGON_STIPPLE)來啟動鏤空模式。(使用glDisable(GL_POLYGON_STIPPLE)可以關閉之)。
2.使用glPolygonStipple()來設置鏤空的樣式。

      void glPolygonStipple(const GLubyte *mask)

      glPolygonStipple() -- set the polygon stippling pattern

      PARAMETERS

    mask Specifies a pointer to a 32*32 stipple pattern that will be unpacked from memory in the same way that glDrawPixels unpacks pixels.

      DESCRIPTION

     Polygon stippling, like line stippling (see glLineStipple), masks out certain fragments produced by rasterization, creating a pattern. Stippling is independent of polygon antialiasing. mask is a pointer to a 32*32 stipple pattern that is stored in memory just like the pixel data supplied to a glDrawPixels with height and width both equal to 32, a pixel format of GL_COLOR_INDEX, and data type of GL_BITMAP. That is, the stipple pattern is represented as a 32*32 array of 1-bit color indices packed in unsigned bytes. glPixelStore parameters like GL_UNPACK_SWAP_BYTES and GL_UNPACK_LSB_FIRST affect the assembling of the bits into a stipple pattern. Pixel transfer operations (shift, offset, pixel map) are not applied to the stipple image, however. Polygon stippling is enabled and disabled with glEnable and glDisable, using argument GL_POLYGON_STIPPLE. If enabled, a rasterized polygon fragment with window coordinates xw and yw is sent to the next stage of the GL if and only if the (xw mod 32)th bit in the (yw mod 32)th row of the stipple pattern is one. When polygon stippling is disabled, it is as if the stipple pattern were all ones.

      此函數定義填充多邊形的當前點畫模式。其中的參數mask指向一個長度為128字節的空間,它表示了一個32*32的矩形應該如何鏤空。其中:第一個字節表示了最左下方的從左到右(也可以是從右到左,這個可以修改)8個像素是否鏤空(1表示不鏤空,顯示該像素;0表示鏤空,顯示其后面的顏色),最后一個字節表示了最右上方的8個像素是否鏤空。

 

    (3)繪制實例:

      1)不同繪圖模式下的對比和反轉的對比:

#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#define Pi 3.1416
void Init(void)
{
    glClearColor(0.5,0.5,0.5,0.5);
    glClear(GL_COLOR_BUFFER_BIT);
}

void paint()
{
    glViewport(0,0,1000,200);

    //先畫分割線
    glColor3f(1.0f,0.0f,0.0f);
    glPointSize(5.0f);
    glBegin(GL_LINES);
    glVertex2f(-0.6f, -1.0f);    glVertex2f(-0.6f, 1.0f);
    glVertex2f( 0.2f, -1.0f);    glVertex2f( 0.2f, 1.0f);
    glEnd();

    GLfloat a = 1 / (4 - 4 * cos(72 * Pi / 180));
    GLfloat bx = a * cos(18 * Pi / 180);
    GLfloat by = a * sin(18 * Pi / 180);
    GLfloat cy = -a * cos(18 * Pi / 180);
    GLfloat
        PointA[2] = { 0, a},
        PointB[2] = { bx , by},
        PointC[2] = { 0.25, cy},
        PointD[2] = { -0.25, cy},
        PointE[2] = { -bx, by };


    //圖1,為只無論正反面繪制頂點
    glViewport(0, 0, 200, 200);
    glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);//前面和后面均為以頂點的方式繪制
    glColor3f(1.0f, 1.0f, 0.0f); glPointSize(5.0f);//黃色的頂點
    glBegin(GL_POLYGON);
    glVertex2fv(PointA);
    glVertex2fv(PointC);
    glVertex2fv(PointE);
    glVertex2fv(PointB);
    glVertex2fv(PointD);
    glEnd();

    //其余的圖,均為正面填充,反面線形
    glPolygonMode(GL_FRONT, GL_FILL);// 設置正面為填充模式
    glPolygonMode(GL_BACK, GL_LINE);// 設置反面為線形模式
    glPointSize(2.0f);
    //圖2和圖三形成對比,一個看得是正面,一個看得是反面
    //圖2
    glViewport(200,0,200,200);
    glFrontFace(GL_CCW);//切換正反面
    glColor3f(0.0, 1.0, 1.0f);
    glBegin(GL_POLYGON);
    glVertex2fv(PointA);
    glVertex2fv(PointC);
    glVertex2fv(PointE);
    glVertex2fv(PointB);
    glVertex2fv(PointD);
    glEnd();
    //圖3
    glViewport(400,0,200,200);
    glColor3f(0.0, 1.0, 1.0f);
    glFrontFace(GL_CW);//切換正反面
    glBegin(GL_POLYGON);
    glVertex2fv(PointA);
    glVertex2fv(PointC);
    glVertex2fv(PointE);
    glVertex2fv(PointB);
    glVertex2fv(PointD);
    glEnd();
    //圖4和圖5形成對比,一個看得是正面,一個看得是反面
    //圖4
    glViewport(600, 0, 200, 200);
    glFrontFace(GL_CCW);//切換正反面
    glColor3f(0.0, 0.0, 1.0f);
    glBegin(GL_POLYGON);
    glVertex2fv(PointA);
    glVertex2fv(PointB);
    glVertex2fv(PointC);
    glVertex2fv(PointD);
    glVertex2fv(PointE);
    glEnd();
    //圖5
    glViewport(800,0,200,200);
    glFrontFace(GL_CW);//切換正反面
    glColor3f(0.0, 0.0, 1.0f); 
    glBegin(GL_POLYGON);
    glVertex2fv(PointA);
    glVertex2fv(PointB);
    glVertex2fv(PointC);
    glVertex2fv(PointD);
    glVertex2fv(PointE);
    glEnd();
    //圖6
    glViewport(1000, 0, 200, 200);
    glFrontFace(GL_CW);//切換正反面
    glColor3f(0.0, 0.0, 1.0f);
    glBegin(GL_POLYGON);
    glVertex2fv(PointE);
    glVertex2fv(PointD);
    glVertex2fv(PointC);
    glVertex2fv(PointB);
    glVertex2fv(PointA);
    glEnd();
    //圖5和圖6形成對比,按不同頂點順序繪制,正反面的定義不同。
    glFlush();
}
int main(int argc,char *argv[])
{
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
    Init();
    glutInitWindowSize(1200,200);
    glutInitWindowPosition(200,200);
    glutCreateWindow("不同繪圖模式下的對比和反轉的對比");
    glutDisplayFunc(paint);
    glutMainLoop();
}

      2)在3D空間中繪制旋轉立方體:

#include<GL/freeglut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

//繪制立方體的八個立體空間中的坐標值,原點即其中點
GLfloat vertexNum[][3] = {
    {  1.0, -1.0, -1.0 },
    {  1.0, -1.0,  1.0 },
    {  1.0,  1.0,  1.0 },
    {  1.0,  1.0, -1.0 },
    { -1.0, -1.0, -1.0 },
    { -1.0, -1.0,  1.0 },
    { -1.0,  1.0,  1.0 },
    { -1.0,  1.0, -1.0 }
};
GLfloat colors[][3] = {
    { 1.0, 0.0, 0.0 },//紅色
    { 1.0, 1.0, 0.0 },//黃色
    { 0.0, 1.0, 0.0 },//綠色
    { 0.0, 1.0, 1.0 },//青色
    { 1.0, 0.0, 1.0 },//品紅色
    { 0.0, 0.0, 1.0 },//淡藍色
    { 0.0, 0.5, 0.0 },//淡綠色
    { 0.0, 0.5, 0.5 },//淡青色

};

float angle = 0;

void polygon(int a, int b, int c, int d)
{   //4個點組成一個面
    glBegin(GL_QUADS);//繪制多組獨立的填充四邊形
    glColor3fv(colors[a]);
    glVertex3fv(vertexNum[a]);
    glColor3fv(colors[a]);
    glVertex3fv(vertexNum[b]);
    glColor3fv(colors[a]);
    glVertex3fv(vertexNum[c]);
    glColor3fv(colors[a]);
    glVertex3fv(vertexNum[d]);
    glEnd();
}

void cube(void)
{
    polygon(0, 3, 2, 1);
    polygon(2, 3, 7, 6);
    polygon(3, 0, 4, 7);
    polygon(1, 2, 6, 5);
    polygon(4, 5, 6, 7);
    polygon(5, 4, 0, 1);
}

void paint(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix();
    {
        glRotatef(angle, 0, 1, 0);
        glColor3f(1, 0, 0);
        glutWireCube(2.0);//繪制邊長為2的線框
        cube();
    }
    glPopMatrix();
    glutSwapBuffers();
}

void IdleFunction()
{
    angle += 0.02;
    if (angle > 36000)
        angle = 0;
    glutPostRedisplay();
}

void reshapeFunction(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-2.0, 2.0, -2.0, 2.0, -8.0, 8.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(3.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
void Init()
{
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.3, 0.3, 0.3, 1.0f);
}
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
    glutInitWindowPosition(200,200);
    glutInitWindowSize(500, 500);
    glutCreateWindow("RotateCube");
    Init();

    glutReshapeFunc(reshapeFunction);
    glutIdleFunc(IdleFunction);
    glutDisplayFunc(paint);
    glutMainLoop();
    return 0;

}

      3)面的鏤空操作的講解實例:

#include <GL/glut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

GLubyte data[128] = {//數組就是一個蒼蠅的圖像。 數組有128個字節, 表示了一個32x32的矩陣型鏤空的數據。數組里面的第一個字節表示了左下方從左到右的8個像素是否鏤空, 以此類推。
    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, 0x33, 0xcc, 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,
};

void display(void)
{
    glClearColor(0.5,0.5,0.5,0.5);
    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_POLYGON_STIPPLE);
    glPolygonStipple(data);
    glRectf(-0.75f, -0.75f, 0.75f, 0.75f);
    glFinish();
}
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(400, 200);
    glutInitWindowPosition(400, 200);
    glutCreateWindow("利用多邊形鏤空繪制蒼蠅");
    glutDisplayFunc(display);
    glutMainLoop();
}

      我們可以自定義進行圖形鏤空的圖形:

      保存為.bmp形式。

#include <GL/glut.h>
#include <stdio.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void display(void)
{
    GLubyte data[128];
    FILE* fp;
    fp = fopen("C:/Users/JMSun/Desktop/圖片/logo.bmp", "rb");
    if (!fp)
    {
        exit(0);
    }
    if (fseek(fp, -(int)sizeof(data), SEEK_END))
    {
        exit(0);
    }
    if (!fread(data, sizeof(data), 1, fp))
    {
        exit(0);
    }
    fclose(fp);

    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_POLYGON_STIPPLE);
    glPolygonStipple(data);
    glRectf(-0.75f, -0.75f, 0.75f, 0.75f);
    glFinish();
}
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(400, 200);
    glutInitWindowPosition(400, 200);
    glutCreateWindow("利自定義logo進行多邊形鏤空");
    glutDisplayFunc(display);
    glutMainLoop();
}

    4)相關知識:

      1.設置顏色:

void glColor3f (GLfloat red, GLfloat green, GLfloat blue);//設置顏色
void glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);//設置顏色, 帶透明通道

      如果需要使用透明通道, 必須要打開ALPHA混合器,並指定源與目標的混合方式:

使用透明通道時,打開ALPHA混合器的方式:
1.glEnable(GL_BLEND); // 打開混合
2.glDisable(GL_DEPTH_TEST); // 關閉深度測試
3.glBlendFunc(GL_SRC_ALPHA, GL_ONE); // 基於源象素alpha通道值的半透明混合函數

     使用ALPHA通道的實例:

#include <GL/glut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void display()
{
    glClearColor(0.5f,0.5f,0.5f,0.5f);
    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_BLEND); // 打開混合
    glDisable(GL_DEPTH_TEST); // 關閉深度測試
    glBlendFunc(GL_SRC_ALPHA, GL_ONE); // 基於源象素alpha通道值的半透明混合函數
    glColor3f(0.0f, 1.0f, 0.0f);
    //glBegin(GL_POLYGON);//運用系統自帶的函數繪制圖形不需要glBegin和glEnd;
    glRectf(-0.6,-0.6,0.6,0.6);
    //glEnd();

    glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    glLineWidth(20.0f);
    glBegin(GL_LINES);
    glVertex2f(-0.25f, 0);
    glVertex2f(0.25f, 0);
    glEnd();
    glFinish();
}
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(400, 200);
    glutInitWindowPosition(400, 200);
    glutCreateWindow("帶透明值Alpha的圖形");
    glutDisplayFunc(display);
    glutMainLoop();
}

 

    (4)實時動態繪制正弦曲線上的點:(在之前的版本基礎上的改動)

#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

#define GL_PI  3.1416f
const GLfloat factor = 0.1f;

GLfloat transferNum = 0.0f;

void SetupRC(void)
{
    glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
}
void paint(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    GLfloat x;
    glColor3f(1.0f, 1.0f, 1.0f);
    glPointSize(4.0f);//應在glBegin之前
    glBegin(GL_POINTS);
    for (x = -10; x<10; x += 0.1)
    {
        glVertex2f(x*factor, sin(x -transferNum)*factor * 5);
    }
    glEnd();
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_LINES);
    glVertex2f(-1.0f, 0.0f);
    glVertex2f(1.0f, 0.0f);
    glVertex2f(0.0f, -1.0f);
    glVertex2f(0.0f, 1.0f);
    glEnd();
    glutSwapBuffers();
}
void IdleFunction()
{
    transferNum += 0.003 / (2 * GL_PI);
    if (transferNum>200*GL_PI){
        transferNum = 0;
     }
    glutPostRedisplay();
}
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//當沒有鍵盤監聽時不需要用雙緩沖模式
    glutInitWindowSize(400, 400);
    glutCreateWindow("動態正弦圖像");
    glutDisplayFunc(paint);
    SetupRC();
    glutIdleFunc(IdleFunction);
    glutMainLoop();

    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM