2012年5月25日 星期五

OpenGL: Starting with the Shading Language-1

since: 2012/05/25
update: 2012/05/25

reference:
1. Tutorial - Getting Started with the OpenGL Shading Language (GLSL)
2. OpenGL: Starting with the Shading Language-0
3. 著色器 (Shader) « 逍遙文工作室

Shader 與 Program Objects

A. 說明:
      1. 開始本篇文章前, 請先看: OpenGL: Starting with the Shading Language-0 .

      2. 本篇文章內容的主要來源為: joshbeam.com GLSL , 在此並不會作全文翻譯,
           主要是對原始程式碼作部分的說明, 並會參考其它相關資料.

      3. 本文除了介紹 OpenGL Shading Language (GLSL) 外, 還展示了:
           漫射/散射(diffuse)鏡射光(specular)的效果.

-----------------------------------------------------------------------------------------

B. GLSL 概觀:
      Shaders(著色器)是在圖形繪製的過程中, 在每個頂點(vertex)或每個像素(pixel)
      上執行的小程式. (也有 geometry<幾何> shaders, 它是用來對基本的幾何圖形
      作操作, 在此文將不會討論到) 相關參考: 著色器 (Shader)         

-----------------------------------------------------------------------------------------

C. Shader 與 Program Objects:
     1. Program objects: 是一個用來容納 shaders容器(container).

     2. Shaders 需要先被編譯(compile)並連結(link), 才可在圖形繪製的過程中使用.

     3. 資料可能會從某個 shader stage 傳到另一個 shader stage (例如: vertex shaders
         經常會將產生的訊息傳送給 fragment shaders 使用); 因此, 將多個 shaders 連結
         到同一個 program object 的機制是必要的.

     4. 一個 shader object 代表一種確定型別的單一 shader(例如: vertex shader
          或 fragment shader).

     5. 一般情況下, 我們會先建立一個 shader object, 將 shader 的 source code
         字串
的方式傳入, 然後將它編譯.

-----------------------------------------------------------------------------------------

D. 編譯一個 GLSL shader:
     
編譯一個 GLSL shader 的方式如下: (需傳入一個包含 source code 的檔案路徑)
      開啓 shader.cpp 檔案:

/*
 * Returns a shader object containing a shader
 * compiled from the given GLSL shader file.
 */
static GLuint
shaderCompileFromFile(GLenum type, const char *filePath)
{
    char *source;
    GLuint shader;
    GLint length, result;

    /* get shader source */
    //
    // shaderLoadSource 函式, 亦定義在此檔案中.
    // 根據給定的檔案路徑(vertex/fragment shader source file)回傳字串內容.
    source = shaderLoadSource(filePath);
    if(!source)
        return 0;

    /* create shader object, set the source, and compile */
    shader = glCreateShader(type);
    length = strlen(source);

    // sets the shader's source
    glShaderSource(shader, 1, (const char **)&source, &length);

    // compile the shader
    glCompileShader(shader);

    // 釋放包含 shader source string 的記憶體
    free(source);

    /* make sure the compilation was successful */
    // 執行錯誤檢查
    glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
    if(result == GL_FALSE) {
        char *log;

        /* get the shader info log */
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
        //@fixed: Assigning to 'char *' from incompatible type 'void *';
        //log = malloc(length);
        log = (char *)malloc(length);

        glGetShaderInfoLog(shader, length, &result, log);

        /* print an error message and the info log */
        fprintf(stderr, "shaderCompileFromFile(): Unable to compile %s: %s\n", filePath, log);

        free(log);

        glDeleteShader(shader);
        return 0;
    }

    // 回傳一個 GLuint 的值, 可用來參照到此 shader object
    return shader;
}

-----------------------------------------------------------------------------------------


E. 將 shader 附加到 program object 上:
     一旦 shader 編譯好後, 就應該將它附加(attached)到一個 program object 上.
     開啓 shader.cpp 檔案:

/*
 * Compiles and attaches a shader of the
 * given type to the given program object.
 */
void
shaderAttachFromFile(GLuint program, GLenum type, const char *filePath)
// 參數說明:
//
// program: shader 要 attach 到那個 program object(可用 glCreateProgram 建立). 
// type: shader 的類型, 例如: GL_VERTEX_SHADER
//           或
GL_FRAGMENT_SHADER.
// filePath: 包含 shader 原始碼的檔案路徑.
{
    /* compile the shader */
    GLuint shader = shaderCompileFromFile(type, filePath);
    if(shader != 0) {
        /* attach the shader to the program */
        glAttachShader(program, shader);

        /* delete the shader - it won't actually be
         * destroyed until the program that it's attached
         * to has been destroyed */
        // 一直要等到 Attach 到的 program object 被 destroyed 了,
        // shader 才會真正被刪除.
        glDeleteShader(shader);
    }
}

-----------------------------------------------------------------------------------------

F. 連結 Program Object:

     一旦 shaders 被 attache 到 program object 後, 就可以用 glLinkProgram 函式
     來連結 program object 了. 
     開啓 scene.cpp 檔案:

void
sceneInit(void)
{
    GLint result;

    /* create program object and attach shaders */
    g_program = glCreateProgram();
    shaderAttachFromFile(g_program, GL_VERTEX_SHADER, "data/shader.vp");
    shaderAttachFromFile(g_program, GL_FRAGMENT_SHADER, "data/shader.fp");

    /* link the program and make sure that there were no errors */
    glLinkProgram(g_program);
    glGetProgramiv(g_program, GL_LINK_STATUS, &result);
    if(result == GL_FALSE) {
        GLint length;
        char *log;

        /* get the program info log */
        glGetProgramiv(g_program, GL_INFO_LOG_LENGTH, &length);
        //@fixed: Assigning to 'char *' from incompatible type 'void *';
        //log = malloc(length);
        log = (char *)malloc(length);
        glGetProgramInfoLog(g_program, length, &result, log);

        /* print an error message and the info log */
        fprintf(stderr, "sceneInit(): Program linking failed: %s\n", log);
        free(log);

        /* delete the program */
        glDeleteProgram(g_program);
        g_program = 0;
    }

    //***************************************************************
    /* get uniform locations */
    //
    // 在 program object 連結好後, 我們可使用 glGetUniformLocation 函式,
    // 到 shaders 裡取得一些被定義的 uniform 變數(在 shaders 原始檔看得到的)
    // 之 locations.

    // (這些 locations 即是 uniform 變數對應到顯示卡 Register 的編號地址)
    //
    // 之後, 將會看到這些 uniform 變數的 locations, 將允許我們傳遞訊息shaders 裡. 
    // 即是: 攝影機位置, 三個光源的位置及顏色的資訊.

    // uniform vec3 cameraPosition;
    g_programCameraPositionLocation = glGetUniformLocation(g_program, "cameraPosition");

    // uniform vec3 lightPosition[NUM_LIGHTS];
    g_programLightPositionLocation = glGetUniformLocation(g_program, "lightPosition");

    // uniform vec3 lightColor[NUM_LIGHTS];
    g_programLightColorLocation = glGetUniformLocation(g_program, "lightColor");

    /* set up red/green/blue lights */
    g_lightColor[0] = 1.0f; g_lightColor[1] = 0.0f; g_lightColor[2] = 0.0f;
    g_lightColor[3] = 0.0f; g_lightColor[4] = 1.0f; g_lightColor[5] = 0.0f;
    g_lightColor[6] = 0.0f; g_lightColor[7] = 0.0f; g_lightColor[8] = 1.0f;

    /* create cylinder */
    // 建立圓柱體
    createCylinder(36);

    /* do the first cycle to initialize positions */
    g_lightRotation = 0.0f;
    sceneCycle();

    /* setup camera */
    g_cameraPosition[0] = 0.0f;
    g_cameraPosition[1] = 0.0f;
    g_cameraPosition[2] = 4.0f;
    glLoadIdentity();
    glTranslatef(-g_cameraPosition[0], -g_cameraPosition[1], -g_cameraPosition[2]);
}


沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。