2012年5月26日 星期六

OpenGL: Starting with the Shading Language-4

since: 2012/05/26
update: 2012/05/26


reference:
1. Tutorial - Getting Started with the OpenGL Shading Language (GLSL)
2. OpenGL: Starting with the Shading Language-0
3. OpenGL: Starting with the Shading Language-1
4. OpenGL: Starting with the Shading Language-2
5. OpenGL: Starting with the Shading Language-3

使用 Shaders

A. 開啓 scene.cpp 檔案:
     // 使用 shader 來渲染(rendering)物體
void
sceneRender(void)
{
    int i;
    // clearing the color and depth buffers
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /* enable program and set uniform variables */
    // 呼叫 glUseProgram, 來使用有附加上 shaders 的 program object
    glUseProgram(g_program);

    //***************************************************************
    // 使用 glUniform3fv 來設定在 shaders 裡 vec3 uniform 變數的值.
    //
    // glUniform3fv(GLint location, GLsizei count, const GLfloat *value);
    //
    // location: uniform 變數的 location
    // count: 該 location 的向量數目
    // value: 包含向量資料的指標(型別: 浮點陣列)
    //
    // g_cameraPosition 和 g_lightColor 的值, 在 sceneInit 函式中設定好了.
    //
    // g_lightPosition 的值, 會在 sceneCycle 函式中持續地改變.
    glUniform3fv(g_programCameraPositionLocation, 1, g_cameraPosition);
    glUniform3fv(g_programLightPositionLocation, NUM_LIGHTS, g_lightPosition);
    glUniform3fv(g_programLightColorLocation, NUM_LIGHTS, g_lightColor);

    /* render the cylinder */
    // 繪製一個帶有頂點位置與法線資料的圓柱體
    glDrawArrays(GL_TRIANGLE_STRIP, 0, g_cylinderNumVertices);

    /* disable program */
    //  將 0 傳給 glUseProgram 函式, 來停用 program object
    glUseProgram(0);

    /* render each light */
    // 繪製一些球體來代表光源
    for(i = 0; i < NUM_LIGHTS; ++i) {
        /* render sphere with the light's color/position */
        glPushMatrix();
        glTranslatef(g_lightPosition[i * 3 + 0], g_lightPosition[i * 3 + 1], g_lightPosition[i * 3 + 2]);

        glColor3fv(g_lightColor + (i * 3));
        glutSolidSphere(0.04, 36, 36);
        glPopMatrix();
    }

    glutSwapBuffers();
}

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

B. 編譯並執行:


OpenGL: Starting with the Shading Language-3

since: 2012/05/26
update: 2012/05/26


reference:
1. Tutorial - Getting Started with the OpenGL Shading Language (GLSL)
2. OpenGL: Starting with the Shading Language-0
3. OpenGL: Starting with the Shading Language-1
4. OpenGL: Starting with the Shading Language-2

Fragment Shader

A. 說明
     1. 光源種類:
         a. Ambient Light(環境光):
             (1). 光線來自四處, 非特定位置所發出的光線.
             (2). 特性: 被照射的物體, 在每個方向, 角度上受到等量的光線.

         b. Diffuse Light(漫射/散射光):
             (1). 有方向性, 均勻照射到表面.
             (2). 例如: 午後太陽光從窗戶射入.

         c. Specular Light(反射/鏡射光):
             (1). 有方向性, 使物體反射到某個角度.
             (2). 與入射角度有關係, 如: 聚光燈.


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

B. 開啓 shader.fp 檔案:
      在此處, 這個 fragment shader 的工作是: 當啟用 program object 時, 對每個要被
      畫出來的 fragment (或 pixel) 計算其散射光(diffuse)鏡射光(specular)的值.

// 常數: 定義光源的數量
const int NUM_LIGHTS = 3;

// 定義環境光源: AMBIENT
//
// 代表該像素點在沒有受到任何光源(在此有三個光源)散射(diffuse)
// 或鏡射(specular)的作用下, 所呈現出來的顏色.
const vec3 AMBIENT = vec3(0.1, 0.1, 0.1);

// 用來計算隨著像素點離光源距離越遠所造成的光線減弱
const float MAX_DIST = 2.5;
const float MAX_DIST_SQUARED = MAX_DIST * MAX_DIST;

// lightColor 包含了從應用程式傳送來的光源顏色(在此有三個光源)
//
// vec3 的 x, y ,z 分量, 分別代表顏色的紅, 綠, 藍之分量.
uniform vec3 lightColor[NUM_LIGHTS];

// varyings 變數的值, 會先在 vertex shader 設定好, 然後被傳送到 fragment shader
// 裡(此處), 在這個過程中, 每個從 vertex shader 傳送到  fragment shader 的資料,
// 在繪製表面的過程中都會被進行內插處理(interpolated).
//
// 目前 fragment 的法線
varying vec3 fragmentNormal;

// 從目前的 fragment 指向攝影機的向量
varying vec3 cameraVector;

// 從目前的 fragment 指向每個光源(在此有三個)位置的向量之陣列
varying vec3 lightVector[NUM_LIGHTS];

// fragment shader 的 main 函式, 它會對每個 fragment 都進行處理.
void
main()
{
    // initialize diffuse/specular lighting
    //
    // 對目前的 fragment 受到光源的總散射(diffuse)與總鏡射(specular)進行初始化,
    // 個別光源所產生的散射(diffuse)與鏡射(specular)之值會分別被加入(到向量裡).
    vec3 diffuse = vec3(0.0, 0.0, 0.0);
    vec3 specular = vec3(0.0, 0.0, 0.0);

    // normalize the fragment normal and camera direction
    //
    // 將從 vertex shader 傳送來的法線, 使用內建的函式: normalize 進行正規化,
    // 因為經過內插處理後的 fragmentNormal, 已不再是單位向量了.
    vec3 normal = normalize(fragmentNormal);

    // 對從 vertex shader 傳送來的 cameraVector, 進行正規化處理.
    vec3 cameraDir = normalize(cameraVector);

    // loop through each light
    //
    // 開始個別計算光源對目前 fragment 所產生的 Diffuse/Specular
    for(int i = 0; i < NUM_LIGHTS; ++i) {

        // calculate distance between 0.0 and 1.0
        //
        // MAX_DIST_SQUARED: 代表 fragment 可接收到光源影響的最遠距離之平方
        //
        // 內建函式 dot: 回傳向量內積的結果.
        // 內建函式 min: 回傳較小的值.
        //
        // dist 的值將介於 0.0 ~ 1.0 之間
        float dist = min(dot(lightVector[i], lightVector[i]), MAX_DIST_SQUARED) / MAX_DIST_SQUARED;

        // 距離係數: distFactor
        //
        // 1. 如果目前的 fragment 距離光源很近, 距離係數會接近 1.0 (完全光照)
        //
        // 2. 如果目前的 fragment 與光源的距離是大於或等於 MAX_DIST_SQUARED,
        //     距離係數會等於 0.0 (完全不受光源影響)
        float distFactor = 1.0 - dist;

        // diffuse
        //
        // 計算光源的散射效果
        //
        // 從目前的 fragment 指向光源的向量被正規化, 並儲存在 lightDir 裡
        vec3 lightDir = normalize(lightVector[i]);

        // fragment 的法線與 lightDir 的內積值儲存在 diffuseDot 裡
        //
        // 如果二個向量指向同一個方向, 內積的結果為 1.0, 會受到完全的散射光影響;
        //
        // 隨著向量夾角的增加, 內積的值逐漸變少, 當二個向量指向完全相反的方向時,
        // 內積為負值(此時, 目前的 fragment 沒有被光源照射到). 
        float diffuseDot = dot(normal, lightDir);

        // 接著, 將內積的結果加總到 diffuse 向量上.
        //
        // 內建函式 clamp: 當內積的結果為負時, diffuse 向量的大小也不會減少.
        diffuse += lightColor[i] * clamp(diffuseDot, 0.0, 1.0) * distFactor;

        // specular
        //
        // 計算光源的鏡射光效果, 鏡射光會在物體表面產生高光點效果.
        //
        // 向量 halfAngle, 是 "從目前的 fragment 到攝影機方向" 和 "從目前的 fragment
        // 到光源位置方向" 的半途(halfway)向量之正規化向量.
        //
        // 鏡射光效果與射光效果類似, 只不過在此處, 會將 fragment 法線比喻為
        // halfAngle 向量, 以取代光源方向的向量, 如下圖所示:
         // 紅球代表被單一光源照射的物體.
         // 紅, 綠, 藍三線相交的點代表目前的單一 fragment.
         // 紅線代表正規化的光源方向之向量.
         // 藍線代表正規化的攝影機方向之向量.
         //
         // 如果把紅, 藍二個向量相加, 結果就是穿越灰色線框指向右上方的綠線.
         // 如果將這條綠線正規化, 結果就是半途(halfway)向量了.
         //
         // 上圖中可以看出, 該 fragment 的半途(halfway)向量, 與該 fragment 的
         // 法線方向是相同的. 因此該  fragment 為全高光區(full specular lighting);
         // 這是有道理的, 因為光源方向與攝影機方向互為 90 度夾角, 而 fragment 的
         // 法線方向在 45 度角的位置.
        vec3 halfAngle = normalize(cameraDir + lightDir);

        // 給鏡射光各顏色分量, 分別增加 0.5 讓其看起來更亮, 
        // 並利用 min 函式, 讓加法後的各顏色分量不會超過 1.0.
        vec3 specularColor = min(lightColor[i] + 0.5, 1.0);

        // 計算鏡射光的內積
        float specularDot = dot(normal, halfAngle);
       
        // 接著, 將內積的結果加總到 specular 向量上.
        // 在此對 specularDot 作指數運算(16次方), 指數越大, 高光區域越集中.
        specular += specularColor * pow(clamp(specularDot, 0.0, 1.0), 16.0) * distFactor;
    }

    // sample 是一個 vec4 類型的變數, 代表 fragment 原來的自然顏色, 在此為白色.
    vec4 sample = vec4(1.0, 1.0, 1.0, 1.0);

    // 輸出 fragment 的顏色
    //
    // 將 sample, diffuse, AMBIENT 和 specular 組合在一起, 產生了該 fragment
    // 最後的顏色,
將此值設定給內建的變數: gl_FragColor, fragment shader 就完成了.   
    gl_FragColor = vec4(clamp(sample.rgb * (diffuse + AMBIENT) + specular, 0.0, 1.0), sample.a);
}

2012年5月25日 星期五

OpenGL: Starting with the Shading Language-2

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. OpenGL: Starting with the Shading Language-1
4. IMGD 4000 (D 09) - Technical Game Development II
    => GPU/Shaders

5. CS 354 (Kilgard, Spring 2012): Programmable Shading

Vertex Shader

A. 說明:
      1. GPU Shaders = programmability + access to GPU internals
          (可程式化, 並可對 GPU 的內部作存取)

      2. Programmable Graphics Pipeline

      3. Vertex Shaders:
         a. Input: Application geometry(幾何) & per vertex attributes
         b. Transform input in a meaningful way
         c. 任務:
             (1). 頂點轉換.
             (2). 法線轉換, 正規化.
             (3). 光源設置.
             (4). 產生貼圖座標與轉換.

      4. Fragment Shaders:
          a. Input: Perspective Correct Attributes (interpolated: 內插法)
          b. Transform input into color or discard
          c. 任務:
              (1). 貼圖存取.
              (2). 霧氣(fog)
              (3). 將 Fragment 丟棄.

      5. Rendering Pipeline:

      6. Vertex Shader Inputs & Outputs:

      7. Fragment Shader Inputs & Outputs:

      8. Type Qualifiers: (變數型別的修飾語)
          => Used to management the input and output of shaders.

           a. attribute: // 從應用程式 input 到 Vertex shader
               Communicate frequently changing variables from the application
               to a vertex shader.

           b. uniform: // 可視為常數
               Communicate infrequently changing variables from the application
                to any shader.

           c. varying: // Vertex 與 fragment shader 的變數傳遞界面
               Communicate interpolated variables from a vertex shader
               to a fragment shader

      9. Qualifiers in pipeline:

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

B. 開啓 shader.vp 檔案:

// 常數: 定義光源的數量
const int NUM_LIGHTS = 3;

//
uniform 變數:
//
// 包含了從 OpenGL 應用程式傳送到 shaders 的資訊.
// 在此處為: 觀察者的相機位置以及包含了每個光源位置的陣列.
//
// 在開始使用 program object 來繪製之前, 必須先在 OpenGL 應用程式設定
// Uniform 變數的值.

//
// vec3 型別: 代表有 3 個元素的向量. 
uniform vec3 cameraPosition;
uniform vec3 lightPosition[NUM_LIGHTS];

// varying 變數:
//
// 這些變數的值經由 vertex shader 設定, 並且傳送給 fragment shader 來使用.
//
// 這些值將在繪製表面時被內插處理, 並且會於 fragment shader 執行
// 光源計算時使用到.
varying vec3 fragmentNormal;
varying vec3 cameraVector;
varying vec3 lightVector[NUM_LIGHTS];

// shader 的 main 函式:
//
// 包含了實際的 shader 程式碼, 將會對所有傳入的頂點都進行處理.
void
main()
{
    // set the normal for the fragment shader and
    // the vector from the vertex to the camera
    //
    //
vertex shader 將頂點法線(vertex normal)藉由 varying 變數: fragmentNormal
   
// , 傳送給 fragment shader.
    //
    // 由於是
varying 變數, 所以它的值在繪製表面的過程中會被作內插處理.
    //
    // 內建的變數:
gl_Normal , 可以讓 vertex shaders 取得頂點的法線
    fragmentNormal = gl_Normal;

    // 內建的變數: gl_Vertex, 包含了目前頂點未轉換之前的位置.
    //
    // varying 變數: cameraVector, 被設定為從目前的頂點指向攝影機位置向量.
    cameraVector = cameraPosition - gl_Vertex.xyz;

    // set the vectors from the vertex to each light
    //
   
// varying 變數: lightVector, 被設定為從目前的頂點指向每個光源向量.
    for(int i = 0; i < NUM_LIGHTS; ++i)
        lightVector[i] = lightPosition[i] - gl_Vertex.xyz;

    // output the transformed vertex
    //
    // 設定內建的變數:
gl_Position, 它包含了頂點轉換之後應該被繪製的位置.
    //
    // 藉由內建的矩陣
gl_ModelViewProjectionMatrix (目前的投影與 model view 矩陣
    // 之乘積
)矩陣, 我們轉換了gl_Vertex.
    //

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

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]);
}


OpenGL: Starting with the Shading Language-0

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

reference:
Tutorial - Getting Started with the OpenGL Shading Language (GLSL)

前置準備

A. 說明:
      本文章內容沿用 OpenGL: Basic Setup 所建立的專案, 來加入新的檔案.
       新加入的檔案來源皆從 joshbeam.com GLSL  取得.

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

B. 下載檔案與專案調整: (前置準備)
      1. 下載檔案: glsl_lighting-1.tar.gz

      2. 解壓縮後, 將所有 *.c 的檔案皆改成 *.cpp (因為我們使用 C++ 的專案), 
          再將 main.cpp 更名成 GLSLmain.cpp (防止專案中名稱重複).

      3. 接著將所有檔案(除了 Makefile 與 README.txt), 包含 data 資料夾,
          全部 copy 到專案目錄下. 

      4. 專案調整:
         a. 在專案建立一個群組: GLSL, 將剛剛 copy 到專案下的檔案全部加到群組裡.

         b. 將之前的 main function (如果有的話) 更改名稱, 不要成為預設的程式入口.
             開啓 main.cpp 檔案, 修改如下:
            // main function for loading 3d models
            //int main(int argc, char * argv[])
            int main_forLoading3dMmodels(int argc, char * argv[])
            {
            ....
            }

         c. 檢查工作目錄:
             (1). 點選專案左上方的 Scheme, 選取: "Edit Scheme..."
             (2). 接著,  先選取左邊的 "Run MyOpenGL Debug", 再選取右邊的: "Options":
                     勾選: Use Custom working directory:
                                /Lanli/RD/Projects/OpenGL/MyOpenGL
                    
> OK

      5. 分析專案內容:
         執行 "Product" > "Analyze":
         > 修正相關錯誤:

*****************************************************

         a. opengl.h // line 34
             message: 'GL/gl.h' file not found

             修正:
#ifndef __OPENGL_H__
#define __OPENGL_H__

//@fixed for Apple:'GL/gl.h' file not found
#ifndef APPLE
#define APPLE
#endif

#ifdef APPLE
    #include <OpenGL/gl.h>
    #include <OpenGL/glext.h>
    #include <GLUT/glut.h>
#else
    #include <GL/gl.h>
    #include <GL/glext.h>
    #include <GL/glut.h>
#endif /* APPLE */

#endif /* __OPENGL_H__ */

*****************************************************

         b. scene.cpp 
            // line 67
            message: Assigning to 'float *' from incompatible type 'void *';

             修正:
    /* generate vertex data */
    //@fixed: Assigning to 'float *' from incompatible type 'void *';
    //v = malloc(sizeof(float) * size);
    v = (float *)malloc(sizeof(float) * size);

            // line 127
            message: Assigning to 'char *' from incompatible type 'void *';

             修正:
        /* 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);

*****************************************************

         c. shader.cpp
            // line 53, message:
            Cannot initialize a variable of type 'char *' with an rvalue of type 'void *'

             修正:
    /* read the entire file into a string */
    while((tmp = fread(buf, 1, blockSize, fp)) > 0) {
        //@fixed: Cannot initialize a variable of type 'char *' with an rvalue of type 'void *'
        //char *newSource = malloc(sourceLength + tmp + 1);
        char *newSource = (char *)malloc(sourceLength + tmp + 1);
        ....

            // line 110
            message: Assigning to 'char *' from incompatible type 'void *';

             修正:
        /* 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);

2012年5月23日 星期三

OpenGL: Loading 3D Models

since: 2012/05/23
update: 2012/05/24

reference:
1. Nate Robins - OpenGL - Tutors

2. Loading 3D models with GLM on OpenGL

A. 說明:
      1. 在此, 沿用 OpenGL: Basic Setup 所建立的專案,
只會在 main.cpp增加
          程式碼.


      2. 這裡將使用 Nate Robins - OpenGL - Tutors 中原始的 GLM library,
          並配合 Loading 3D models with GLM on OpenGL 的例子來實作.

      3. 3D Models 檔案來源:
          GLM: an Alias Wavefront OBJ file library , 下載 Sample data

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

B. 取得原始的 GLM library:
      1. 到網站: Nate Robins - OpenGL - Tutors 下載檔案:
           Tutors source code package (tutors-src.zip 920KB)

      2. 解壓縮後, 將 glm.hglm.c 加到專案裡; 因為我們所建立的是 C++ 的專案,
          因此將 glm.c 更名為: glm.cpp .

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

C. 加入 3D Models 檔案, 調整專案 Scheme:
     1. 在專案根目錄下新增 "objs" 資料夾, 並將之前下載的 "3D Models 檔案",
         解壓縮後將 f-16.objf-16.mtl copy 到 "objs" 資料夾下.

     2. 在專案中, 加入剛剛的 f-16.objf-16.mtl 檔案.  

     3. 點選專案左上方的 Scheme, 選取: "Edit Scheme..."
         接著,  先選取左邊的 "Run MyOpenGL Debug", 再選取右邊的: "Options":
         勾選: Use Custom working directory: /Lanli/RD/Projects/OpenGL/MyOpenGL
         > OK

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

D. 開啓 main.cpp 檔案, 修改如下:
#include <iostream>
//@add
#include <OpenGL/OpenGL.h> // OpenGL Library
#include <GLUT/GLUT.h> // OpenGL Utility Toolkit 
#include "CallbackFunctions.h"
#include "draw.h"

/************************************/
/* start: add for loading 3d modles */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "glm.h"
#define PI 3.1416

// variaveis
// for 位移: glTranslatef(x,y,z);
float x = 0, y = 0, z = 0;
// for 旋轉: glRotatef(angle, 1.0, 0.0, 0.0);
float angle = 0;

// OpenGL 順序的指令記錄
int displayList = 0;

// 光源位置
GLfloat lightPos[4] = {50.0, 30.0 ,0.0, 0.0};

// 環境光(暗灰色)
GLfloat lightAmb[3] = {0.114, 0.086, 0.031};       

// 漫射光(亮黃色)
GLfloat lightDiff[3] = {0.976, 0.902, 0.761};      

// 3D model
GLMmodel *f16;

/* end: add for loading 3d modles */
/**********************************/


// 畫出 X, Y, Z 軸
void drawAxis(void)
{   
    glPushAttrib(GL_CURRENT_BIT); // add: 2012/05/24

    glLineWidth( 4.0f );
    glBegin(GL_LINES);
   
    // X 軸: 紅色
    glColor3f(1, 0, 0);
    glVertex3f(5, 0, 0);
    glVertex3f(-10, 0, 0);
   
    // Y 軸: 綠色
    glColor3f(0, 1, 0);
    glVertex3f(0, 10, 0);
    glVertex3f(0, -5, 0);
   
    // Z 軸: 藍色
    glColor3f(0, 0, 1);
    glVertex3f(0, 0, 5);
    glVertex3f(0, 0, -10);   
   
    // 將顏色恢復為白色
    //glColor3f(1, 1, 1);
   
    glEnd();

    glPopAttrib(); // add: 2012/05/24
}


// 鍵盤事件
void myKeyboard(unsigned char key, int xx, int yy) {
    switch(key) {
        // 位移: glTranslatef(x,y,z);   
        case 'a' : x -= 2; break; // 往負 X 軸移動 2 單位.
        case 'd' : x += 2; break; // 往正 X 軸移動 2 單位.
        case 's' : z += 2; break; // 往正 Z 軸移動 2 單位.
        case 'w' : z -= 2; break; // 往負 Z 軸移動 2 單位.
        case '.' : y -= 2; break; // 往負 Y 軸移動 2 單位.
        case ',' : y += 2; break; // 往正 Y 軸移動 2 單位.
           
        // 旋轉: glRotatef(angle, 1.0, 0.0, 0.0);    
        case 'k' : angle -= 2.0; break; // 往正 X 軸旋轉角度減少 2 單位.(順時針)
        case 'i' : angle += 2.0; break; // 往正 X 軸旋轉角度增加 2 單位.(逆時針)
        case 27  : exit(0); // esc 離開
    }
    // refresh the screen
    glutPostRedisplay();
}


// 更新視窗
void changeSize(int w, int h) {
    if(h == 0) h = 1;
    float ratio = 1.0* w / h;
   
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glViewport(0, 0, w, h);
    gluPerspective(25, ratio, 1, 1000);
    glMatrixMode(GL_MODELVIEW);
}


// drawing
void renderScene(void) {
    // 畫布背景顏色: 水藍
    glClearColor(0.473f, 0.633f, 0.742f, 0.0f);
   
    // clear the color and depth buffers
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   
    glLoadIdentity();
    gluLookAt(15.0, 30.0,    15.0,        // eye position
              0.0,    5.0,    0.0,        // look at this point
              0.0f,    1.0f,    0.0f);        // "up" vector
   
    // 畫出 X, Y, Z 軸
    drawAxis(); 
   
    // 設定光線
    glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb); // 環境光
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiff); // 漫射光
   
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   
    // 位移
    glTranslatef(x,y,z);
   
    // 旋轉
    glRotatef(angle, 1.0, 0.0, 0.0); 
   
    glCallList(displayList);
       
    // swap the double buffer
    glutSwapBuffers();
}


// main function for loading 3d models
int main(int argc, char * argv[])
{
    // Init glut
    glutInit(&argc, argv);
   
    // Display mode
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
   
    // Init Window
    glutInitWindowPosition(100,100);
    glutInitWindowSize(800,600);
    glutCreateWindow("Loading 3D Models");
       
    // Callback Functions Registration
    glutDisplayFunc(renderScene);
    glutKeyboardFunc(myKeyboard);
    glutIdleFunc(renderScene);
    glutReshapeFunc(changeSize);
   
    // enable settings
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);   
   
    // Loading Model
    f16 = (GLMmodel*)malloc(sizeof(GLMmodel));
    f16 = glmReadOBJ((char *)"objs/f-16.obj");
   
    displayList = glGenLists(1);
    glNewList(displayList, GL_COMPILE);
    //glmList(f16, GLM_SMOOTH);
    glmDraw(f16, GLM_SMOOTH);
    glEndList();
   
    // enter event processing loop
    glutMainLoop();
   
    return 0;
}

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


E. 分析專案內容:
    1. 執行 "Product" > "Analyze":

    2. 修正相關錯誤:
       a. 發現錯誤訊息:
            glm.cpp => Conversion from string literal to 'char *' is deprecated

       b. 修正: 開啓 glm.cpp 檔案, 修改如下:
          // line: 448
    /* make a default group */
    //group = glmAddGroup(model, "default");
    //@fixed: Conversion from string literal to 'char *' is deprecated
    group = glmAddGroup(model, (char *)"default");
         
----------------------------------------------------------------------------------------

F. 編譯並執行:
    說明: 1. 三軸: X 軸紅色, Y 軸綠色, Z 軸藍色.

              2. "位移" 按鍵功能: // glTranslatef(x,y,z); 
                   a: 往負 X 軸移動 2 單位.
                   d: 往正 X 軸移動 2 單位.
                   s: 往正 Z 軸移動 2 單位.
                   w: 往負 Z 軸移動 2 單位.
                   .: 往負 Y 軸移動 2 單位.
                   ,: 往正 Y 軸移動 2 單位.

              3. "旋轉" 按鍵功能: // glRotatef(angle, 1.0, 0.0, 0.0); 
                     k:
往正 X 軸旋轉角度減少 2 單位.(順時針)
                     i: 往正 X 軸旋轉角度增加 2 單位.(逆時針)
                
              4. "離開" 按鍵功能:
                    esc: 離開