3D引擎:Horde3D:如何解析Shader文件(一)

在上一帖中,http://blog.csdn.net/jinghouxiang/article/details/49994983

說了 如何從 Android的jni層獲取 Assets文件夾下 文件的路徑名,獲取路徑名後 需要將文件中的數據讀取到char 數組中

假如 assets下 有一文件爲 Shader/test.shader
rootDir 爲我們讀取文件的根目錄: assets/
這樣定義 加載 test.shader數據的 函數如下
bool ParseText::loadShader(std:: string &filename)
{
       fullFileName = rootDir +filename; 
      LOG_INFO( "ParseText","begin to open file" );
       zip_file * file = zip_fopen( apkArchive, fullFileName .c_str(), 0);

       if(file == NULL)
      {
             return false ;
      }

      LOG_INFO( "ParseText","end open file" );
       //char data[];
       int size = file->bytes_left ;
       char *data = (char *)malloc( sizeof(char )*size);
       int state;


      state = zip_fread(file, ( void *)data, size);
      LOG_INFO( "ParseText","begin to read file: %d, %d" , state,  size);
       if(state == 0)
      {
             return false ;
      }
      data[size]= '\0';


       if(!load(data, size))
      {
             return false ;
      }


       return true ;
}


 以上使用libzip的 兩個api,
zip_open 從 解壓的 apk中 獲取 路徑名爲 fullFileName的文件;
zip_fread,讀取 字節數 爲 size大小 的 文件內容到 char數組中

以上代碼的 load(const char *data, int size) 函數,即爲 Horde3D Resource類的 load函數,功能一樣,都是對加載的數據進行處理。

Shader類 作爲 Resource類的子類,其load函數主要是對  shader文件進行處理:
假設shader文件內容如下:
[[FX]]

// Samplers
sampler2D albedoMap;

// Contexts
context OVERLAY
{
     BlendMode = Blend;
}

[[VS_OVERLAY]]

varying vec2 texCoords;


[[FS_OVERLAY]]

uniform vec4 olayColor;

可以看出,Horde3D的  shader文件寫法 有3塊:
 1 FX section:
     這一部份 爲 一個 總體的概括,主要說明 shader的內容涉及到的 渲染管線流程
 2 VS_OVERLAY section:
   這一部分爲 自定義的 頂點着色器的部分
 3 FS_OVERLAY section:
   這一部分爲自定義的  片段着色器的部分

在load 函數中 首先要對shader文件 進行每個section的分割,然後再調用parse函數 對 每塊進行解析,賦予相應屬性的值。

下面,着重說下,Horde3D是如何對 shader文件的內容進行 分段,以及分離出每句的:

 對 Shader文件的內容進行 分段:
 bool ParseText::load( const char *data, int size)
{
       //Parse sections
       const char *pData = data;
       const char *eof = data +size;

       while (pData <eof)
      {
             if (pData < eof-1 && *pData == '['&&*(pData+1)== '[' )
            {
                  pData += 2;

                   //Parse section name
                   const char *sectionNameStart = pData;
                   while (pData <eof && *pData != ']' && *pData != '\n' &&*pData !='\r' )++pData;
                   const char *sectionNameEnd = pData++;

                   //Check for correct closing of name
                   if (pData >= eof || *pData++!=']' ) return false;

                   //Parse content
                   const char *sectionContentStart = pData;
                   while ((pData < eof && *pData != '[')||(pData < eof-1&&*(pData+1)!='[' ))++pData;
                   const char *sectionContentEnd = pData;

                   if (sectionNameEnd - sectionNameStart == 2 &&
                              *sectionNameStart == 'F' &&*(sectionNameStart+1)=='X' )
                  {
                         //FX section
                         if (fxCode != 0x0) return false ;
                         fxCode = new char[sectionContentEnd - sectionContentStart+1];
                         memcpy ( (void *)fxCode , sectionContentStart, sectionContentEnd - sectionContentStart );
                         fxCode [sectionContentEnd - sectionContentStart] = '\0' ;
                  } else
                  {

                  }
            } else
                  ++pData;
      }<pre name="code" class="plain">

  load函數在一開始,會去掉文本的 註釋部分。根據文本的定義, 我們可以看出每個section開頭 :都有個 形如 [[ NAME  ]]的標題,  load函數裏 就是根據這個 標題 [[]]的特性 來解析出每部分。解析出 FX部分後,將 FX的 內容 賦給 fxCode 。 後面  頂點着色器 和 片段着色器的 內容也是一樣 它們 會分別 讀取 存在 字符串裏給 CodeResource進行處理 。
 如果,給屬性附上某個屬性值,還需要對每段的內容進行分句,拿到每一句後,才能進一步確定它是哪個值, 這主要是靠Tokenizer 這個內部類:
protected 變量:
     int _line;   //  1開始,記錄文本中的行數
     const char *_p;   // 指向文本中的字符數據,起始位置爲文本首地址
 
    char  _token[tokenSize], _prevToken[tokenSize]; // getNextToken() 每次去取出的字符串, _token 表示當前取出的字符串
    static const ptrdiff_t  tokenSize = 128;  // 表示取出的字符串的最大字符數 
     
 
  protected 方法:
     void checkLineChange():
       當遇到 '\r', '\n'會 換行, 對 line自動+1, p指針指向下一行
     void skip(const char *chars):
        當p指向的字符 在chars字符串裏時,就跳到下一行。 如果不在,p指針就停止向前
    bool seekChar(const char *chars);
        它與 skip相反, 當p指向的字符不在 chars裏時,會跳到下一行
       
     void getNextToken()
       這是重點要對待的api
       它首先使用 skip(" \t\n\r") 跳過所有空格
       然後 如果有 雙引號的內容 ,  通過分別確定 " 和"的 地址 將引號之間的內容取出來
    如果沒有 
      則 當遇到  空格 \t \n \r { } () <> = , ; 這些字符時 p會停止,  然後 根據前後指向的地址把內容取出來
    拿以上文本舉例子, getNextToken會取一下內容:
 albedoMap
     ;
     context
     OVERLAY
     {
     BlendMode
      =
      Blend
      ;
     }
    .....
通過對以上 Tokenizer的分析,就可以看出   如何 將 Shader文本的內容 賦給 shader 變量的屬性。  




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章