2019年1月28日 星期一

Electron搭配WebGL使用的Bug

Electron搭配WebGL使用的Bug

前言

  最近使用Electron搭配WebGL後發生了問題,在此做個紀錄,方便日後追蹤。

內容

  在Electron使用WebGL2.0的Sampler object功能時發現無法正常工作,在Chrome71(Win64)版本時可以正常工作,但換成是Electron後就沒法正常工作,以下為Electron的版本資訊
node 10.11.0, Chrome 69.0.3497.106, Electron 4.0.1

sampler object的設定如下
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_S, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_T, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_R, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(samplerObject , GL_TEXTURE_MIN_FILTER, GL_NEAREST);

設定完後,顯示沒有Mipmap的Texture時,會發生顯示全黑的圖片,推測是沒產生Mipmap而造成的,但GL_TEXTURE_MIN_FILTER的參數是GL_NEAREST,不應該去取Mipmap值!目前解決的方法是用WebGL1.0的方法來設定Texture的參數,這樣可以正常工作,若忘記WebGL1.0的方法可以參考關於SamplerObject

2019/08/02 更新

  更新到 Electron6 後,可以修正這個問題,更新後的Electron的版本資訊
node 12.4.0, Chrome 76.0.3809.88, Electron 6.0.0

相關文章

關於SamplerObject

Electron4的使用範例

Electron4的使用範例

前言

  最近將遊戲引擎放到Electron後發現會報錯,查了一下是Electron4的初始化方式有一點不一樣,在此做個紀錄。

內容

  如果使用初探Electron裡的範例用在Electron4的話,會收到2個Warning,內容如下
Electron Deprecation Warning (nodeIntegration default change) This window has node integration enabled by default. In Electron 5.0.0, node integration will be disabled by default. To prepare for this change, set {nodeIntegration: true} in the webPreferences for this window, or ensure that this window does not rely on node integration and set {nodeIntegration: false}.
//
Electron Security Warning (Insecure Content-Security-Policy) This renderer process has either no Content Security
    Policy set or a policy with "unsafe-eval" enabled. This exposes users of
    this app to unnecessary security risks.

google後發現Electron4有更改初始化的方法,可以參考Electron 4.0.0,而舊的範例使跑在Electron1.8,官網的教學並沒有更新,Electron4的範例如下
const {app, BrowserWindow} = require('electron')
  const path = require('path')
  const url = require('url')
  
  // 將這個 window 物件記在全域變數裡。
  // 如果沒這麼做,這個視窗在 JavaScript 物件被垃圾回收時(GC)後就會被自動關閉。
  let win
  
  function createWindow () {
    // 建立瀏覽器視窗。
    win = new BrowserWindow({
      width: 800, 
      height: 600,
      webPreferences:{
        nodeIntegration: true
      }
    });
  
    // 並載入應用程式的 index.html。
    win.loadURL(url.format({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
    }))
  
    // 打開 DevTools。
    win.webContents.openDevTools()
  
    // 視窗關閉時會觸發。
    win.on('closed', () => {
      // 拿掉 window 物件的參照。如果你的應用程式支援多個視窗,
      // 你可能會將它們存成陣列,現在該是時候清除相關的物件了。
      win = null
    })
  }
  
  
  // 當 Electron 完成初始化,並且準備好建立瀏覽器視窗時
  // 會呼叫這的方法
  // 有些 API 只能在這個事件發生後才能用。
  app.on('ready', createWindow)
  
  // 在所有視窗都關閉時結束程式。
  app.on('window-all-closed', () => {
    // 在 macOS 中,一般會讓應用程式及選單列繼續留著,
    // 除非使用者按了 Cmd + Q 確定終止它們
    if (process.platform !== 'darwin') {
      app.quit()
    }
  })
  
  app.on('activate', () => {
    // 在 macOS 中,一般會在使用者按了 Dock 圖示
    // 且沒有其他視窗開啟的情況下,
    // 重新在應用程式裡建立視窗。
    if (win === null) {
      createWindow()
    }
  })
  process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
  // 你可以在這個檔案中繼續寫應用程式主程序要執行的程式碼。 
  // 你也可以將它們放在別的檔案裡,再由這裡 require 進來。

紅字的部分是新增的,新增完後就可以向舊版一樣使用。

參考資料

Why do I see an “Electron Security Warning” after updating my Electron project to the latest version?
Electron 4.0.0

相關文章

初探Electron

2019年1月21日 星期一

關於SamplerObject

關於SamplerObject

前言

  SamplerObject是在OpenGL3.3以後新增的功能,可以用來簡化設定Texture參數的過程,在此將學習過程做個紀錄。

內容

  在OpenGL設定Texture的參數範例如下
GLint textureLoc;
GLenum textureType;
GLuint texture;
glActiveTexture(GL_TEXTURE0);
glBindTexture(textureType,texture);
glTexParameteri(textureType, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(textureType, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(textureType, GL_TEXTURE_WRAP_R, GL_REPEAT);
glTexParameteri(textureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(textureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glUniform1i(textureLoc,(GLint)0);

這是在沒有SamplerObject的功能時必須要在每次設定Texture時都要經過的流程,在有SamplerObject後的範例如下
//Create sampler object
GLuint samplerObject = 0;
glGenSamplers(1,&samplerObject );
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_S, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_T, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_R, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(samplerObject , GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//On set up texture
GLint textureLoc;
GLenum textureType;
GLuint texture;
glActiveTexture(GL_TEXTURE0);
glBindTexture(textureType,texture);
glBindSampler(textureLoc,samplerObject );

glUniform1i(textureLoc,(GLint)0);

//On delete
glDeleteSamplers(1,&samplerObject );

要Create SamplerObject時,透過glGenSamplers()來製造,並透過glSamplerParameteri()來設定,設定完後,以後在設定Texture參數時透過glBindSampler(),要綁並到哪張Texture,會透過Texture的Location來決定,用起來相當容易,釋放的部分就和往常一樣,用glDeleteSamplers()來釋放即可。

2019/08/24更新

  上述範例有錯,請更正為以下
//Create sampler object
GLuint samplerObject = 0;
glGenSamplers(1,&samplerObject );
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_S, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_T, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_WRAP_R, GL_REPEAT);
glSamplerParameteri(samplerObject , GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(samplerObject , GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//On set up texture
GLint textureLoc;
GLenum textureType;
GLuint texture;
GLuint textureID=0;
glActiveTexture(GL_TEXTURE0+textureID);
glBindTexture(textureType,texture);
glBindSampler(textureID,samplerObject );

glUniform1i(textureLoc,(GLint)0);

//On delete
glDeleteSamplers(1,&samplerObject );

舊的範例在 glBindSampler() 會使用 textureLoc 作為綁定,但這用法是錯的!正確的用法是用 textureID 來綁定, textureID 指的是 glActiveTexture() 所指定的ID,如當 ID 是 GL_TEXTURE0 對應的 textureID 就是0,當 ID 是 GL_TEXTURE1 對應的 textureID 就是1,其他依此類推。

參考資料

OpenGL 3D繪圖互動程式設計,出版者:旗標,作者:賴祐吉, 姚智原, 朱宏國作,
ISBN-13:9789863125112

2019年1月14日 星期一

關於VertexArrayObject(VAO)

關於VertexArrayObject(VAO)

前言

  VertexArrayObject是從OpenGL3.0以後加入的特性,最近發現我對它的用法有錯誤的見解,在此做個紀錄。

內容

  最近發現到我對VertexArrayObject的用法有誤,會有錯誤來自於範例很容易是以下
void main()
{
  //
  //...
  //Set up vao
  GLuint vao;
  glGenVertexArrays(1,&vao);
  glBindVertexArray(vao);
  //Set up vertex attribute
  glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
  glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)(sizeof(float)*9));
  glEnableVertexAttribArray(0);
  glEnableVertexAttribArray(1);
  //
  //...
  return 0;
}

範例鐘用註解分為"Set up vao"與"Set up vertex attribute"兩個部分,在沒有VAO的特性時,只需要做"Set up vertex attribute"的部分,當有VAO的特性時就必須多做"Set up vao"的部分,這一直是我之前的理解,雖然用起來沒問題,但用法卻很容易被誤解。誤解了什麼?誤解的部分是在每個Frame要設定繪圖資料時,會執行glBindVertexArray()與"Set up vertex attribute"的部分,這個想法是錯的!正確的是每個Frame只需執行glBindVertexArray()即可,"Set up vertex attribute"的部分不需再次執行,VAO像是個"紀錄"的物件,當綁過glVertexAttribPointer()與glEnableVertexAttribArray()後會記錄下來,下次只要glBindVertexArray()後就等於做了上次的綁定,這樣可以省下每次設定物件的glVertexAttribPointer()與glEnableVertexAttribArray()。最後來比較一下用法,首先是沒有VAO特性時的用法
//Frame1  
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)(sizeof(float)*9));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//Frame2
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)(sizeof(float)*9));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//Frame3
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)(sizeof(float)*9));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

接著是有VAO特性時的用法
//Frame1  
glBindVertexArray(vao);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(void*)(sizeof(float)*9));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//Frame2
glBindVertexArray(vao);
//Frame3
glBindVertexArray(vao);


參考資料

OpenGL 3D繪圖互動程式設計,出版者:旗標,作者:賴祐吉, 姚智原, 朱宏國作,
ISBN-13:9789863125112

2019年1月7日 星期一

3D空間坐標系認定的問題

3D空間坐標系認定的問題

前言

  3D空間坐標系在入門的時候似乎說得很清楚,有分為左手系與右手系,但最近發現以前學到的判定方法有問題,在此做個紀錄。

內容

  先說說我先前的認定方法,先看下圖
有問題的空間坐標系認定法

當X軸與Y軸的固定後看Z的方向來決定是左手或右手,但這個方法在拿來判定Unreal4的時候,會被判定為"左手系",但事實不然,查找Unreal4講述空間座標的文件後,發現一種新的坐標系,名為正交座標系,這時才發現以前的判定法有問題必須改變判定法。

  判定坐標系時,之前只考慮軸的方向而已,這次要多考慮旋轉的方向來判定,如圖
判定軸的旋轉方向
把眼睛對著軸的方向後,看當給予該軸正旋轉值時,會是"順時針"或"逆時針",如果多加上這個原則後,左手系的每個軸的旋轉方向都會是"順時針",右手系則是每個軸的旋轉方向都是"逆時針",Unreal4的X軸與Y軸的旋轉方向是"逆時針",但Z軸的旋轉方向卻是"順時針",
如下圖
坐標系比較圖
正交系的判定目前我還沒找到,但至少不會再把Unreal4判為左手系了。

  這裡順便把目前用的3D軟體的坐標系列出來:
Unity3D:左手系
Blender3D:右手系
Unreal4:正交坐標系
Lumberyard:右手系
CryEngine:右手系
看來是右手系多一點。坐標系是否有優劣之分?Unreal4為何採用正交座標系的理由在文件裡並沒有說明,這個問題就留到未來在解決。

參考資料

笛卡兒坐標系
Unreal4講述空間座標的文件
正交座標系