1. 程式人生 > >openGL之glsl入門3--正弦函式疊加為方波

openGL之glsl入門3--正弦函式疊加為方波

    使用GLSL畫點,畫線,畫面,與原來使用glBegin(), glEnd()的方式有類似的地方,原來方式api比較多,GLSL採用的陣列一次傳送,程式的本質還是座標點的設計與確認,只要知道圖怎麼畫,哪種方式差異不大,本章主要介紹:

1. 正弦函式的基本畫法

2. 鍵盤的控制

3. uniform變數的用法

4. 正弦波疊加為方波的GLSL實現

    GLSL畫這些基本的型別是,使用的函式主要是glDraw*系列的函式,這裡再說一下:

void  glDrawArrays (GLenum mode, GLint first, GLsizei count);

    mode與老的方式一致,有以下型別,

畫點GL_POINTS,畫線 GL_LINES,順連線段GL_LINE_STRIP,迴環線段GL_LINE_LOOP,三角形GL_TRIANGLESGL_TRIANGLE_STRIPGL_TRIANGLE_FAN,四邊形GL_QUADSGL_QUAD_STRIP,多邊形GL_POLYGONfirst0即可,count表示要繪製頂點的個數,這個上章說的比較多。

    點面的畫法的例子比較多,請大家自己去練(可以找老的程式改用GLSL方式實現來練手)。

說明:嚴格的說,以上關於新老繪製方式的描述並不準確,openGL繪製方式一直在改進,這裡主要表達的意思是大家需區分使用shader的方式繪製與直接繪製方式的異同,詳細的繪製演進過程,可以看一下這篇文章的介紹:

     上一章的helloworld程式非常簡單,這裡通過正弦波的畫法來實現一個稍微複雜一點的shader程式,讓大家儘快感受shader程式設計。

1. 正弦波繪製

    正弦波公式y = sin(x ) ,公式大家都熟悉,怎麼畫出來呢?這裡我確定了以下幾個引數來畫正弦波:

1. sampleCnt:取樣點個數,openGL畫東西都採用逼近的方式,取樣點越多,正弦波就越精細。

2. factor:用來控制正弦波的頻率,如sin(2x ),sin(3x ) 等。

3. amplitude:振幅,用來控制正弦波的振幅,如3sin(2x )

4. rangeL:我們要把正弦波對映到[-1.0,1.0]

的範圍內,否則畫出來的正弦波都看不到。

5. rangeR:如傳的-pi~pi的範圍,會把這個範圍的正弦波對映到[-1.0,1.0]範圍內。

    注意:shader裡面y座標統一乘了0.9,主要是避免圖形頂到邊框,程式碼如下,可以改引數效果:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
#define PI          3.14159265
#define SAMPLE_CNT  200
 
typedef struct
{
    GLfloat x;
    GLfloat y;
}Point;
static const GLchar * vertex_source =
    "#version 330 core\n"
    "layout (location = 0) in vec2 position;\n"
    "void main()\n"
    "{\n"
        "gl_Position = vec4(position.x,position.y*0.9,0.0,1.0);\n"
    "}\0";
void loadShader(GLuint program,GLuint type,const GLchar * source)
{
    const GLchar * shaderSource[] = {source};
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, shaderSource, 0);
    glCompileShader(shader);
    glAttachShader(program, shader);
}
void init()
{
    GLuint program = glCreateProgram();
    loadShader(program,GL_VERTEX_SHADER,vertex_source);
    glLinkProgram(program);
        glUseProgram(program);
        
        glClearColor(0.5f,0.5f, 1.0f, 1.0f);    
}
Point * createSinArray(GLint sampleCnt,GLfloat factor,GLfloat amplitude,GLfloat rangeL,GLfloat rangeR)
{
    int i = 0;
    GLfloat range = rangeR-rangeL;
    Point * array = NULL;
   
    if((sampleCnt <= 4) || (rangeR <= rangeL))
    {
        printf("param error sampleCnt:%d rangeR:%f rangeL:%f\n",sampleCnt,rangeL,rangeR);
        return NULL;
    }   
    array = (Point * )malloc(sampleCnt * sizeof(Point));
        for(i = 0;i<sampleCnt;i++)
        {
                /* x座標按取樣點均勻的分佈在[-1.0,1.0]的範圍內*/
                array[i].x = (2.0*i-sampleCnt)/sampleCnt;
                /* y座標考慮到了振幅,頻率因素的影響*/
                array[i].y = amplitude*sin(factor*(rangeL+i*range/sampleCnt));
        //printf("array[%d]:%f-%f\n",i,array[i].x,array[i].y);
        }  
   
    return array;
}
void deletSinArray(Point * array)
{
    if(array)
    {
        free(array);
    }
}
void display()
{
        int i = 0;
        glClear(GL_COLOR_BUFFER_BIT);
   
        Point * sinaArray = createSinArray(SAMPLE_CNT,1.0,1.0,-3*PI,3 * PI);
        if( sinaArray)
        {
                glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,sizeof(Point), (GLvoid *)sinaArray);
                glEnableVertexAttribArray(0);   
                glDrawArrays(GL_LINE_STRIP, 0, SAMPLE_CNT);
                deletSinArray(sinaArray);
        }
        glFlush();
        
}
int main(int argc, char * argv[])
{
    glutInit(&argc, argv); 
    glutInitDisplayMode(GLUT_RGBA);
    glutInitWindowPosition(200, 200);
    glutInitWindowSize(300,300);
        glutCreateWindow("article3");
    glewInit();
    init();
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

結果如下:

 

2. 按鍵捕獲

    每次改引數需要編譯才能看效果還是很不方便,下面的例子加入了鍵盤控制,並加入了正弦波合成方波的處理,可以使用箭頭鍵移動正弦波,使用上下箭頭進行振幅調整,使用+-號來調整正弦波疊加的次數。

傅立葉函式分解方波公式:

f(y) = 4/PI * (sinx+ sin3x/3 + sin5x/5 + ...);

實際程式裡面公式為:

f(y) = sinx+ sin3x/3 + sin5x/5 + ...

    學習GLSL的同時,順便來熟悉一下傅立葉函式,想起一句話,只要努力,彎的也能掰成直的(咳,不是我說的,有興趣的可以搜一下傅立葉掐死教程)。鍵盤輸入捕獲主要使用一下兩個函式:

void glutKeyboardFunc(void(*func)(unsigned char key,int x,int y));
void glutSpecialFunc(void (*func)(int key,int x,int y));

    glutKeyboardFunc能捕獲普通的按鍵(數字,字母),而glutSpecialFunc用來捕獲方向鍵,F10等特殊鍵,這兩句話加到glutMainLoop之前就可以了。

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
#define PI          3.14159265
#define SAMPLE_CNT  200
 
typedef struct
{
    GLfloat x;
    GLfloat y;
}Point;
static const GLchar * vertex_source =
    "#version 330 core\n"
    "layout (location = 0) in vec2 position;\n"
        "uniform mat4 matrix;\n"
    "void main()\n"
    "{\n"
        "gl_Position = vec4(position.x,position.y*0.9,0.0,1.0);\n"
    "}\0";
void loadShader(GLuint program,GLuint type,const GLchar * source)
{
    const GLchar * shaderSource[] = {source};
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, shaderSource, 0);
    glCompileShader(shader);
    glAttachShader(program, shader);
}
void init()
{
    GLuint program = glCreateProgram();
    loadShader(program,GL_VERTEX_SHADER,vertex_source);
    glLinkProgram(program);
        glUseProgram(program);
        
        glClearColor(0.5f,0.5f, 1.0f, 1.0f);    
}
Point * createSinArray(GLint sampleCnt,GLfloat factor,GLfloat amplitude,GLfloat rangeL,GLfloat rangeR)
{
    int i = 0;
    GLfloat range = rangeR-rangeL;
    Point * array = NULL;
  
    if((sampleCnt <= 4) || (rangeR <= rangeL))
    {
        printf("param error sampleCnt:%d rangeR:%f rangeL:%f\n",sampleCnt,rangeL,rangeR);
        return NULL;
    }   
    array = (Point * )malloc(sampleCnt * sizeof(Point));
        for(i = 0;i<sampleCnt;i++)
        {
                array[i].x = (2.0*i-sampleCnt)/sampleCnt;
                array[i].y = amplitude*sin(factor*(rangeL+i*range/sampleCnt));
        //printf("array[%d]:%f-%f\n",i,array[i].x,array[i].y);
        }  
   
    return array;
}
void deletSinArray(Point * array)
{
    if(array)
    {
        free(array);
    }
}
/*
* @param sinCnt:正弦波疊加次數
* @param sampleCnt:取樣點數,越多,正弦波越精細
* @param amplitude:疊加後的振幅
* @param rangeL:左邊界座標
* @param rangeR:右邊界座標
*/
Point * createSquareWave(GLint sinCnt,GLint sampleCnt,GLfloat amplitude,GLfloat rangeL,GLfloat rangeR)
{
        int i = 0,j = 0;
        Point * array = (Point * )calloc(sampleCnt,sizeof(Point));
        for(i = 0;i<sinCnt;i++)
        {
                int f = 2*i+1;
                /* 依次疊加正弦波,注意頻域為奇數*/
                Point * sinaArray = createSinArray(sampleCnt,1.0*f,1.0/f,rangeL,rangeR);
                for( j = 0;j<sampleCnt;j++)
                {
                        array[j].x = sinaArray[j].x;
                        array[j].y += (sinaArray[j].y*amplitude);       
                }
                deletSinArray(sinaArray);
        }
        return array;
}
int g_sinCnt = 3;
GLfloat g_rangeL = -3*PI,g_rangeR = 3 * PI;
GLfloat g_amplitud = 1.0;
void display()
{
        glClear(GL_COLOR_BUFFER_BIT);
   
        Point * squareWaveArray = createSquareWave(g_sinCnt,SAMPLE_CNT,g_amplitud,g_rangeL,g_rangeR);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,sizeof(Point), (GLvoid *)squareWaveArray);
    glEnableVertexAttribArray(0);       
    glDrawArrays(GL_LINE_STRIP, 0, SAMPLE_CNT);  
        deletSinArray(squareWaveArray);
        glFlush();
}
void keyboard(unsigned char key, int x, int y)
{
        switch(key)
        {
        case '-':
                if( g_sinCnt > 1) g_sinCnt--;
                break;
        case '=':
        case '+':
                if( g_sinCnt < 50) g_sinCnt++;
                break;
        default:
                break;
        }
        printf("g_sinCnt:%d g_rangeL:%f g_rangeR:%f g_amplitud:%f\n",g_sinCnt,g_rangeL,g_rangeR,g_amplitud);
        glutPostRedisplay();
}
void specialKey(GLint key,GLint x,GLint y)
{
        switch(key)
        {
        case GLUT_KEY_UP:
                if( g_amplitud < 2)     g_amplitud += 0.1;
                break;
        case GLUT_KEY_DOWN:
                if( g_amplitud > 0.3)   g_amplitud -= 0.1;
                break;
        case GLUT_KEY_LEFT:     
                g_rangeL -= 0.1;g_rangeR -= 0.1;
                break;
        case GLUT_KEY_RIGHT:
                g_rangeL += 0.1;g_rangeR += 0.1;
                break;
        default:
                break;
        }
        printf("g_sinCnt:%d g_rangeL:%f g_rangeR:%f g_amplitud:%f\n",g_sinCnt,g_rangeL,g_rangeR,g_amplitud);
        glutPostRedisplay();
}
int main(int argc, char * argv[])
{
    glutInit(&argc, argv); 
    glutInitDisplayMode(GLUT_RGBA);
    glutInitWindowPosition(200, 200);
    glutInitWindowSize(300,300);
        glutCreateWindow("article3");
    glewInit();
    init();
    glutDisplayFunc(display);
        glutKeyboardFunc(keyboard);
        glutSpecialFunc(specialKey);
    glutMainLoop();
    return 0;
}

    大家可以按方向鍵調整位置和振幅,通過+-鍵控制疊加次數。

    疊加次數4次效果:

 

    疊加次數50次效果:

 

    方波波形不完善的原因應該與取樣精度不足、疊加次數不夠及單浮點精度不足有關。

3. uniform變數

    以上程式問題大家應該看到了,效率太低,根本是用CPU來畫正弦函式,計算基本在CPU上完成的,GPU的浮點計算與平行計算的優勢完全沒有發揮出來,有什麼辦法可以把浮點運算挪到GPU裡去算呢?這個實現不復雜,有一個問題需先解決,除了向GPU傳遞頂點座標外,我們還需要向GPU傳遞控制資訊,如上面例子的方向鍵與+-的控制資訊,這個可以通過uniform變數實現。

    uniform變數使用流程如下:

1. shader程式定義uniform變數

2. 客戶程式使用glGetUniformLocation函式獲取uniform變數的索引(控制代碼、描述符)

3. 使用 glUniform**(程式裡用 glUniform1fglUniform1i就可以了)把uniform變數值傳送到shader程式,shader就可以用這個值了。

不同型別的glUniform**函式引數差異還挺大的,都不用記,可以直接查API用法:

注意:uniformin的區別需要弄清楚,紅寶書2.3.2節說的比較詳細。uniform 特點是所有shader都可以使用,且在shader裡不可修改,in重資料傳遞,uniform 主要用於控制傳遞。

#include <GL/glew.h>
#include <GL/glut.h>
static const GLchar * vertex_source =
    "#version 330 core\n"
        "uniform float translate_x;\n"
        "uniform float translate_y;\n"
    "layout (location = 0) in vec2 position;\n"
    "void main()\n"
    "{\n"
        "gl_Position = vec4(position.x+translate_x,position.y+translate_y,0.0,1.0);\n"
    "}\0";
void loadShader(GLuint program,GLuint type,const GLchar * source)
{
    const GLchar * shaderSource[] = {source};
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, shaderSource, 0);
    glCompileShader(shader);
    glAttachShader(program, shader);
}
 
void init()
{
        GLfloat translate_x_index,translate_y_index;
    GLuint program = glCreateProgram();
    loadShader(program,GL_VERTEX_SHADER,vertex_source);
    glLinkProgram(program);
        glUseProgram(program);
        
        /* 獲取uniform變數索引(位置),注意名稱要和shader中的保持一致*/
        translate_x_index = glGetUniformLocation(program, "translate_x");
        translate_y_index = glGetUniformLocation(program, "translate_y");
        /* 通過索引把資訊傳到GPU,供shader使用*/
        glUniform1f(translate_x_index,-0.6);
        glUniform1f(translate_y_index,0.3);
        glClearColor(0.5f,0.5f, 1.0f, 1.0f);
}
void display()
{
        glClear(GL_COLOR_BUFFER_BIT);
        GLfloat vertices[] = {0.0, 0.0,0.5,0.5,0.5,0.0};
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid *)vertices);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
 
        glFlush();
}
int main(int argc, char * argv[])
{
    glutInit(&argc, argv); 
    glutInitDisplayMode(GLUT_RGBA);
    glutInitWindowPosition(200, 200);
    glutInitWindowSize(300,300);
        glutCreateWindow("HelloWord");
    glewInit();
    init();
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}
    可以看到相比第二章的例子,shader裡增加了兩個uniform變數,init時先通過變數名獲取uniform變數位置(一般叫位置,有的也稱為索引),再通過位置把資料傳送給GPU,整體過程還是挺簡單的,大家可以改glUniform1f 傳的值看效果,效果如下:

 

    glUniform*函式用來更新uniform變數,原型有很多,總的來說函式原型分成下3大類

1. 變數操作: glUniform{1|2|3|4}{f|i|d|ui}(GLint location,TYPE value);

2. 陣列操作:glUniform{1|2|3|4}{f|i|d|ui}v(GLint location,GLsizei count,const TYPE * value);

3. 矩陣陣列操作: glUniformFloatMatrix{2|3|4}fv(GLint location,GLsizei count,GLboolean transpose,const TYPE * value);

    其中f=floati = intd=doubleui=unsigned int TYPE為對應的型別,v結尾的都是陣列型別。下面列的都是4維的函式原型:

void glUniform4i(GLint location,GLint v0,GLint v1,GLint v2,GLint v3);
void glUniform4f(GLint location,GLfloat v0,GLfloat v1,GLfloat v2,GLfloat v3);
void glUniform4iv(GLint location,GLsizei count,const GLint *value);
void glUniform4fv(GLint location,GLsizei count,const GLfloat *value);
void glUniformMatrix4fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat *value);

    矩陣操作只提供了陣列方式的操作,矩陣方式有引數transpose引數,為true時,以行主序方式讀入(c語言陣列方式),false為列主序方式讀取(shader中,矩陣預設以列為主序,自己組矩陣的時候需注意)。

Uniform變數陣列的操作例子如下:

mat4 model_matrix[8] = {...};
glUniformMatrix4fv(render_model_matrix_loc, 8, GL_FALSE, model_matrix[0]);

注意:count的值需與矩陣陣列一致,否則傳送的資料不對,紅寶書第三章例子ch03_drawcommands.cpp中,count的值(應為1,程式為4)不正確導致程式一片黑。

model_matrix = vmath::translation(-3.0f, 0.0f, -5.0f);
glUniformMatrix4fv(render_model_matrix_loc, 4, GL_FALSE, model_matrix);
glDrawArrays(GL_TRIANGLES, 0, 3);

4. 正弦波疊加的GLSL實現

    通過uniform變數傳送控制資訊到shader中,就可以使用shader來實現本章的正弦疊加的例子。對於複雜的例子,使用shader檔案的方式更好閱讀,為方便大家編譯,本章的例子先用notepad++寫好,再複製上來的,閱讀的時候,大家可以把分號去掉,比較方便。

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
#define PI          3.14159265
#define SAMPLE_CNT  200
static const GLchar * vertex_source =
"#version 330 core                                                                                      \n"
"layout (location = 0) in float vertexSerial;                                                           \n"
"uniform int g_sinCnt;                                                                                  \n"
"uniform float g_rangeL;                                                                                \n"
"uniform float g_rangeR;                                                                                \n"
"uniform float g_amplitud;                                                                              \n"
"const int sampleCnt=200;                                                                               \n"
"                                                                                                       \n"
"vec2 createSinPostion(float posIdex,float factor,float amplitude, float rangeL, float rangeR)          \n"
"{                                                                                                      \n"
"    vec2 sinPos;                                                                                       \n"
"    float range = rangeR - rangeL;                                                                     \n"
"                                                                                                       \n"
"    sinPos.x = (2.0 * posIdex - sampleCnt)/sampleCnt;                                                  \n"
"    sinPos.y = amplitude * sin(factor * (rangeL + posIdex * range / sampleCnt));                       \n"
"                                                                                                       \n"
"    return sinPos;                                                                                     \n"
"}                                                                                                      \n"
"                                                                                                       \n"
"vec2 createSquareWave(float posIdex,int sinCnt, float amplitude, float rangeL, float rangeR)           \n"
"{                                                                                                      \n"
"    vec2 SquareWarvePos, sinPos;                                                                       \n"
"    int i = 0;                                                                                         \n"
"                                                                                                       \n"
"    for(i = 0;i<sinCnt;i++)                                                                            \n"
"    {                                                                                                  \n"
"        int f = 2 * i + 1;                                                                             \n"
"                                                                                                       \n"
"        sinPos = createSinPostion(posIdex, 1.0 * f, 1.0 / f, rangeL, rangeR);                          \n"
"        SquareWarvePos.x = sinPos.x;                                                                   \n"
"        SquareWarvePos.y += (sinPos.y * amplitude);                                                    \n"
"    }                                                                                                  \n"
"                                                                                                       \n"
"    return SquareWarvePos;                                                                             \n"
"}                                                                                                      \n"
"                                                                                                       \n"
"void main()                                                                                            \n"
"{                                                                                                      \n"
"    vec2 SquareWarvePos  = createSquareWave(vertexSerial,g_sinCnt,g_amplitud,g_rangeL,g_rangeR);       \n"
"    gl_Position = vec4(SquareWarvePos,0.0,1.0);                                                        \n"
"}                                                                                                      \n"
"\0";
void loadShader(GLuint program, GLuint type, const GLchar * source)
{
    const GLchar * shaderSource[] = {source};
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, shaderSource, 0);
    glCompileShader(shader);  
    glAttachShader(program, shader);
}
/* uniform控制變數的位置定義*/
GLint sinCntIdx;
GLfloat rangeLIdx,rangeRIdx,amplitudIdx;
/* uniform控制變數的值定義*/
GLint g_sinCnt = 3;
GLfloat g_rangeL = -3 * PI,g_rangeR = 3 * PI,g_amplitud = 1.0;
void init()
{
    GLuint program = glCreateProgram();
    loadShader(program, GL_VERTEX_SHADER, vertex_source);
    glLinkProgram(program);
    glUseProgram(program);
    /* 獲取shader中uniform變數位置*/
     sinCntIdx = glGetUniformLocation(program, "g_sinCnt");
    rangeLIdx = glGetUniformLocation(program, "g_rangeL");
    rangeRIdx = glGetUniformLocation(program, "g_rangeR");
    amplitudIdx = glGetUniformLocation(program, "g_amplitud");
    glClearColor(0.5f, 0.5f, 1.0f, 1.0f);
}
void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    /* 向shader中uniform變數傳送值*/
    glUniform1i(sinCntIdx,g_sinCnt);   
    glUniform1f(rangeLIdx,g_rangeL);
    glUniform1f(rangeRIdx,g_rangeR);
    glUniform1f(amplitudIdx,g_amplitud);
    /* 頂點的座標由shader自己生成,但需告知shader點的索引*/
    GLfloat vertexSerial[SAMPLE_CNT];
    for(int i = 0;i<SAMPLE_CNT;i++)
    {
        vertexSerial[i] = i;
    }
    glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, sizeof(GLfloat), (GLvoid *)vertexSerial);
    glEnableVertexAttribArray(0);
    glDrawArrays(GL_LINE_STRIP, 0, SAMPLE_CNT);
    glFlush();
}
void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
        case '-':
            if (g_sinCnt > 1) g_sinCnt--;
            break;
        case '=':
        case '+':
            if (g_sinCnt < 50) g_sinCnt++;
            break;
        default:
            break;
    }
    printf("g_sinCnt:%d g_rangeL:%f g_rangeR:%f g_amplitud:%f\n", g_sinCnt, g_rangeL, g_rangeR, g_amplitud);
    glutPostRedisplay();
}
void specialKey(GLint key, GLint x, GLint y)
{
    switch (key)
    {
        case GLUT_KEY_UP:
            if (g_amplitud < 2)    g_amplitud += 0.1;
            break;
        case GLUT_KEY_DOWN:
            if (g_amplitud > 0.3)    g_amplitud -= 0.1;
            break;
        case GLUT_KEY_LEFT:
            g_rangeL -= 0.1;
            g_rangeR -= 0.1;
            break;
        case GLUT_KEY_RIGHT:
            g_rangeL += 0.1;
            g_rangeR += 0.1;
            break;
        default:
            break;
    }
    printf("g_sinCnt:%d g_rangeL:%f g_rangeR:%f g_amplitud:%f\n", g_sinCnt, g_rangeL, g_rangeR, g_amplitud);
    glutPostRedisplay();
}
int main(int argc, char * argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);
    glutInitWindowPosition(200, 200);
    glutInitWindowSize(300, 300);
    glutCreateWindow("article3");
    glewInit();
    init();
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);
    glutSpecialFunc(specialKey);
    glutMainLoop();
    return 0;
}

    執行的結果(效果)與客戶端實現是一致的,效率上應該會更高一些,這裡就不貼截圖了。

    大家可以看到使用shader語言來實現正弦疊加函式,shader的編寫與客戶端程式碼編寫最大的不同是shader處理的是單個點座標, 程式中去掉了取樣點迴圈,程式更簡潔,語法和C語言基本一致,客戶端程式碼移植到shader中去,只需做簡單的修改即可。

    客戶度向GPU只傳送了取樣點的序列,傳送的資料小很多,如果使用vbovaodisplay傳送資料部分,可以進一步簡化和提高效率。

注意:

1. 目前除錯shader程式沒什麼好的辦法,紅寶書shader的例子都很簡短,除錯長一點的shader,請大家先把loadShader裡的錯誤處理加上。

2. GLintGLfloat等是openGL客戶端的變數型別,shader裡面用的都是intfloat等,與c類似的未封裝型別。

3. GLSL內建了很多函式,上述的sin函式就是內建的,內建函式原型用的時候可以查手冊(紅寶書附錄C),避免與cc++的庫函式搞混了。