用OpenGL繪制平滑着色的三角形與相交區域的混合着色


一、三角形的繪制

在OpenGL中,面是由多邊形構成的。三角形可能是最簡單的多邊形,它有三條邊。可以使用GL_TRIANGLES模式通過把三個頂點連接到一起而繪出三角形。

使用GL_TRIANGLE_STRIP模式可以繪制幾個相連的三角形,系統根據前三個頂點繪制第一個多邊形,以后每指定一個頂點,就與構成上一個三角形的后兩個頂點繪制形的一個三角形。

使用GL_TRIANGLE_FAN模式可以繪制一組相連的三角形,這些三角形繞着一個中心點成扇形排列。

第一個頂點構成扇形的中心,用前三個頂點繪制會最初的三角形后,隨后的所有頂點都和扇形中心以及緊跟在它前面的頂點構成下一個三角形,此時是以順時針方向穿過頂點。

 

二、繞法

在繪制三角形的過程中,三個頂點將三角形封閉的過程是有序的,即三角形的構成路徑具有方向性,我們把指定頂點時順序和方向的組合稱為"繞法"。繞法是任何多邊形圖元的一個重要特征。

一般默認情況下,OpenGL認為逆時針繞法的多邊形是正對着的,這一特性對於希望給多邊形的正面和背面賦予不同的物理特性十分有用。

如果要反轉OpenGL的默認行為,調用glFrontFace(GL_CW);GL_CW告訴OpenGL應該把順時針纏繞的多邊形正對着的。為了改回把逆時針繞法視為正面,可以使用GL_CCW。

 

三、明暗處理

在繪制多邊形時,我們常常指定繪制的顏色,而在OpenGL中,顏色實際上是對各個頂點而不是對各個多邊形指定的。

多邊形的輪廓或者內部用單一的顏色或許多不同的顏色來填充的處理方式成為明暗處理。

在OpenGL中,用單一顏色處理的稱為平面明暗處理(FlatShading),用許多不同顏色處理的稱為光滑明暗處理(Smooth Shading),也稱為Gourand明暗處理(Gourand Shading).設置明暗處理模式的函數void glShadeModel(GLenum mode)參數mode取值為GL_FLAT或GL_SMOOTH。

應用平面明暗處理模式時,多邊形內每個點的法向一致,其顏色也一致,OpenGL用指定多邊形最后一個頂點時的顏色作為填充多邊形的純色。

應用光滑明暗處理模式時,多邊形所有點的法向是有內插生產的,具有一定的連續性,因此每個點的顏色也相應內插,故呈現不同色。這種模式下,插值方法采用的是雙線性插值法。

Gourand明暗處理通常算法為:先用多邊形頂點的光強線性插值出當前掃描線與多邊形邊交叉處的光強,然后再用交點的光強線插值處掃描線位於多邊形內區段上每一像素處的光強值。

采用Gourand明暗處理不但可以使用多邊形表示的曲面光強連續,而且計算量很小。這種算法還可以以增量的形式改進,且能用硬件直接實現算法,從而廣泛用於計算機實時圖形生成。

 

四、多邊形的模式

多邊形不是必須用當前顏色填充的。默認情況下繪制的多邊形是實心的,但可以通過指定把多邊形繪制為輪廓或只是點(只畫出頂點)來修改這項默認行為。

函數glPolygonMode(Glenum face,Glenum mode);允許把多邊形渲染為填充的實心,輪廓線或只是點。

另外,可以把這項渲染模式應用到多邊形的兩面或只應用到正面或背面。參數face指定多邊形的哪一面受模式改變的影響——GL_FRONT,GL_BACK或GL_FRONT_AND_BACK。

參數mode用於指定新的繪圖模式。GL_FILL是默認值,生成填充的多邊形;GL_LINE生成多邊形的輪廓;而GL_POINT只畫出頂點。

GL_LINE和GL_POINT繪制的點和線受glEdgeFlag所設置邊緣標記的影響。

 

五、多邊形的繪制規則

在使用大量多邊形構造一個復雜表面時,有兩條重要規則。

第一條規則是所有多邊形都必須是平面的,也就是說,多邊形的所有頂點必須唯有一個平面上,不能在空間中扭曲

第二條規則是多邊形的邊緣不能相交,而且多邊形必須是凸的。

 

六、相交區域的混合着色

glBlendFunc( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ); // 是最常使用的。
若源色為 ( 1.0 , 0.9 , 0.7 , 0.8 )
源色使用 GL_SRC_ALPHA為0.8,

即 0.8*1.0 , 0.8*0.9 , 0.8*0.8 , 0.8*0.8
結果為 0.8 , 0.72 , 0.64 , 0.56

 

目標色為 ( 0.6 , 0.5 , 0.4 , 0.3 )
目標色使用GL_ONE_MINUS_SRC_ALPHA
即 1 - 0.8 = 0.2
0.2*0.6 , 0.2*0.5 , 0.2*0.4 , 0.2*0.3
結果為 0.12 , 0.1 , 0.08 , 0.06

由此而見,使用這個混合函數,源色的α值決定了結果顏色的百分比。
這里源色的α值為0.8,即結果顏色中源色占80%,目標色占20%。

 

將多邊形從遠至近排列,並使用以下函數
glBlendFunc( GL_SRC_ALPHA_SATURATE , GL_ONE );
glEnable( GL_POLYGON_SMOOTH );
可以優化多邊形反走樣;但必須有α位平面,以用來存儲累加的覆蓋值。

 

七、混合 3D 物體

混合 3D 物體時,基本原理和混合 2D 物體一樣,但需要將深度檢測關閉或設置為只讀。

因為深度檢測會剔除被遮擋的部分物體。

glEnable( GL_DEPTH_TEST ); // 啟用深度緩存
glDisable( GL_DEPTH_TEST ); // 禁用深度緩存

glDepthMask( GL_FALSE ); // 深度緩存為 只讀
glDepthMask( GL_TRUE ); // 深度緩存為 讀/寫

 

八、示例程序如下

Github源碼地址

#include "stdafx.h"
//三角形的繪制程序
#include <GL/glut.h>

//旋轉參數
static GLfloat xRot=0.0f;
static GLfloat yRot=0.0f;

//確定多邊形繞法的方向
bool bWinding = true;

//初始化窗口
void init()
{
    //設置窗口背景顏色為黑色
    glClearColor(0.0f,0.0f,0.0f,1.0f);
}

//顯示函數:繪制幾何體的命令
void display()
{
    //清除背景
    glClear(GL_COLOR_BUFFER_BIT);

    glPushMatrix();  //把當前變換矩陣壓入堆棧

    glRotatef(xRot,1.0f,0.0f,0.0f);//使整個場景繞着x軸旋轉
    glRotatef(yRot,0.0f,1.0f,0.0f);//使整個場景繞着y軸旋轉

    //設置點的大小和線寬
    glPointSize(5);
    glLineWidth(5);

    //設置多邊形繞法的方向是順時針還是逆時針
    if (bWinding)
    {
        glFrontFace(GL_CW);   //CW---clock wise順時針
    }
    else
    {
        glFrontFace(GL_CCW);  //CCW---
    }

    //繪制三角形
    glBegin(GL_TRIANGLES);
        glColor3f(0.8f,1.0f,0.0f);
        glVertex3f(0,30,30);

        glColor3f(1.0f,0.0f,0.0f);
        glVertex3f(-40,-40,40);

        glColor3f(0.0f,0.0f,1.0f);
        glVertex3f(40,-40,0);

        glColor3f(0.0f,0.0f,1.0f);
        glVertex3f(60,-20,0);

        glColor3f(0.0f,0.7f,1.0f);
        glVertex3f(30,-60,0);
    glEnd();

    glBegin(GL_LINES);
        glVertex3f(-100.3, 10.2, 10.5);
        glVertex3f(60, -10.2, -11);
    glEnd();

    glPopMatrix();  //還原

    glutSwapBuffers();//刷新命令緩沖區
}

void reshape(int w,int h)
{
    if (h == 0)
    {
        h = 1;
    }
    glViewport(0,0,w,h);
    
    glMatrixMode(GL_PROJECTION);  //矩陣模式:投影視圖
    glLoadIdentity();
    if (w <= h)
{
    //正交參數設置:x=[-100, 100], y=[-100.0*h/w, 100.0*h/w], z=[-100,100]
        glOrtho(-100.0f,100.0f,-100.0f*h/w,100.0f*h/w,-100.0f,100.0f);
    }
    else
    {
        glOrtho(-100.0f*w/h,100.0f*w/h,-100.0f,100.0f,-100.0f,100.0f);
    }

    glMatrixMode(GL_MODELVIEW);  //矩陣模式:模型視圖
    glLoadIdentity();
}

//由鍵盤控制旋轉
void SpecialKeys(int key,int x,int y)
{
    if (key == GLUT_KEY_UP) 
        xRot -=5.0f;
    if (key == GLUT_KEY_DOWN) 
        xRot +=5.0f;
    if (key == GLUT_KEY_LEFT) 
        yRot -=5.0f;
    if (key == GLUT_KEY_RIGHT) 
        yRot +=5.0f;
    
    if (xRot > 356.0f) 
        xRot = 0.0f; 
    if (xRot < -1.0f) 
        xRot = 355.0f; 
    if (yRot > 356.0f) 
        yRot = 0.0f; 
    if (yRot < -1.0f) 
        yRot = 355.0f;
    glutPostRedisplay();//刷新窗口
}

//菜單處理
void ProcessMenu(int value)
{
    switch(value)
    {
    case 1:
        {
            //修改多邊形正面為填充模式
            glPolygonMode(GL_FRONT,GL_FILL);
            break;
        }
    case 2:
        {
            //修改多邊形正面為線模式
            glPolygonMode(GL_FRONT,GL_LINE);
            break;
        }
    case 3:
        {
            //修改多邊形正面為點填充模式
            glPolygonMode(GL_FRONT,GL_POINT);
            break;
        }
    case 4:
        {
            //修改多邊形背面為填充模式
            glPolygonMode(GL_BACK,GL_FILL);
            break;
        }
    case 5:
        {
            //修改多邊形背面為線模式
            glPolygonMode(GL_BACK,GL_LINE);
            break;
        }
    case 6:
        {
            //修改多邊形背面為點填充模式
            glPolygonMode(GL_BACK,GL_POINT);
            break;
        }
    case 7:
        {
            //設置多邊形的陰影模式為平面明暗模式
            glShadeModel(GL_FLAT);
            break;
        }
    case 8:
        {
            //設置多邊形的陰影模式為光滑明暗模式
            glShadeModel(GL_SMOOTH);
            break;
        }
    case 9:
        {
            bWinding = !bWinding;
            break;
        } 
    default:
        break;
    }
    //提交修改並強制重新繪圖
    glutPostRedisplay();
}

void main()
{    
    init();    
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);  //雙緩沖
    glutCreateWindow("多邊形演示");

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutSpecialFunc(SpecialKeys);//設置特殊鍵響應回調函數

    //菜單的句柄
    int nModeMenu;
    int nMainMenu;
    int nColorMenu;

    //創建一個子菜單並定於菜單回調函數
    nModeMenu = glutCreateMenu(ProcessMenu);
    //添加菜單項,1表示選擇菜單條目時傳遞的參數值
    glutAddMenuEntry("正面多邊形填充模式",1);
    glutAddMenuEntry("正面線框模式",2);
    glutAddMenuEntry("正面點模式",3);
    glutAddMenuEntry("反面多邊形填充模式",4);
    glutAddMenuEntry("反面線框模式",5);
    glutAddMenuEntry("反面點模式",6);
    
    //添加一個子菜單
    nColorMenu = glutCreateMenu(ProcessMenu);
    glutAddMenuEntry("平面明暗模式",7);
    glutAddMenuEntry("光滑明暗模式",8);

    //創建主菜單
    nMainMenu = glutCreateMenu(ProcessMenu);
    glutAddSubMenu("多邊形模式",nModeMenu);
    glutAddSubMenu("顏色模式",nColorMenu);
    glutAddMenuEntry("改變繞法",9);
 
    //將創建的菜單與右鍵關聯,即把菜單設置為右鍵彈出式菜單
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    
    glutMainLoop();
}
三角形的繪制程序

 

 

#include <GL/glut.h>
#include <stdlib.h>

static int leftFirst = GL_TRUE;

/*  Initialize alpha blending function.
 */
static void init(void)
{
   glEnable (GL_BLEND);
   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   //glBlendFunc(GL_ONE, GL_ONE);
   glShadeModel (GL_FLAT);
   glClearColor (0.0, 0.0, 0.0, 0.0);
}

static void drawLeftTriangle(void)
{
   /* draw yellow triangle on LHS of screen */

   glBegin (GL_TRIANGLES);
      glColor4f(1.0, 0.0, 0.0, 0.55);
      glVertex3f(0.1, 0.9, 0.0); 
      glVertex3f(0.1, 0.1, 0.0); 
      glVertex3f(0.7, 0.5, 0.0); 
   glEnd();
}

static void drawRightTriangle(void)
{
   /* draw cyan triangle on RHS of screen */

   glBegin (GL_TRIANGLES);
      glColor4f(0.0, 1.0, 0.0, 0.55);
      glVertex3f(0.9, 0.9, 0.0); 
      glVertex3f(0.3, 0.5, 0.0); 
      glVertex3f(0.9, 0.1, 0.0); 
   glEnd();
}

void display(void)
{
   glClear(GL_COLOR_BUFFER_BIT);

   if (leftFirst) {
      drawLeftTriangle();
      drawRightTriangle();
   }
   else {
      drawRightTriangle();
      drawLeftTriangle();
   }

   glFlush();
}

void reshape(int w, int h)
{
   glViewport(0, 0, (GLsizei) w, (GLsizei) h);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
//2D正交投影參數
   if (w <= h) 
      gluOrtho2D (0.0, 1.0, 0.0, 1.0*(GLfloat)h/(GLfloat)w);
   else 
      gluOrtho2D (0.0, 1.0*(GLfloat)w/(GLfloat)h, 0.0, 1.0);
}

void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 't':
      case 'T':
         leftFirst = !leftFirst;
         glutPostRedisplay();    
         break;
      case 27:  /*  Escape key  */
         exit(0);
         break;
      default:
         break;
   }
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
   glutInitWindowSize (200, 200);
   glutCreateWindow (argv[0]);
   init();
   glutReshapeFunc (reshape);
   glutKeyboardFunc (keyboard);
   glutDisplayFunc (display);
   glutMainLoop();
   return 0;
}
相交區域的混合着色

Github源碼地址

 


免責聲明!

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



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