頂点シェーダとフラグメントシェーダを別ファイルで管理したほうがよい
頂点シェーダとフラグメントシェーダを<script>タグ内に書く例をよく見かけますが、
あれは初心者向けの例であり、あまりよくないと思います。(プログラムを公開している人は勿論分かっている。)
頂点シェーダは xxx.vert、フラグメントシェーダは xxx.frag とするのが一般的なようです。
テキストファイルの動的読み込み
.vert も.frag も単なるテキストファイルなので、非同期で読み込むメソッドを作っておくとよいでしょう。
私の作成したメソッドを貼り付けておきます。適宜修正して使ってください。
/** * Load text file * @param {string} url URL * @returns {Promise<string>} string of file contents */ static async loadTextAsync(url) { const response = await fetch(url); return await response.text(); }
#include文を使いたい
C/C++のような#include文でメインのソースに違うソースを挿入できると便利ですよね。
作成したので良ければ使ってください。
ただし、挿入するソースの#includeは無視します。要は#includeを含むソースは#includeできないってことです。
エラーメッセージは修正したほうが良いと思います。
/** * Get shader * @param {WebGL2RenderingContext} gl WebGL2 context * @param {string} mainUrl main source * @param {Object<string, string>} insertUrls insert sources * @returns {Promise<WebGLShader>} Shader */ static async getShader(gl, mainUrl, insertUrls = {}) { // Get insert file contents const inserts = {}; const names = Object.keys(insertUrls); // Is duplicated if(new Set(names).size !== names.length) { throw 'Invalid parameters. (GLUtility.getShader)'; } for(let i = 0; i < names.length; i += 1) { const name = names[i]; inserts[name] = await File.loadTextAsync(insertUrls[name]); } // Insert file let source = await File.loadTextAsync(mainUrl); let includes = source.match(/^#include {1,}"\w{1,}.\w{1,}"/gm); if(!includes) { includes = []; } // Is same count if(includes.length !== names.length) { throw 'Invalid parameters. (GLUtility.getShader)'; } includes.forEach(e => { const fileName = e.match(/"\w{1,}.\w{1,}"/)[0].replaceAll('"', ''); if(!inserts[fileName]) { throw 'Invalid parameters. (GLUtility.getShader)'; } source = source.replace(e, inserts[fileName]); }); // Create shader let type; if(mainUrl.toLowerCase().endsWith('.vert')) {// vertex shader type = gl.VERTEX_SHADER; } else if(mainUrl.toLowerCase().endsWith('.frag')) {// fragment shader type = gl.FRAGMENT_SHADER; } else { throw 'Invalid parameters. (GLUtility.getShader)'; } const shader = gl.createShader(type); // Compile shader gl.shaderSource(shader, source); gl.compileShader(shader); if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { throw gl.getShaderInfoLog(shader); } return shader; }
このgetShaderの呼び出し方の例です。
'glsl/main.vert' には #include "inserted.vert" が記述されている必要があります。
getShader(gl, gl, 'glsl/main.frag', { 'test.frag': 'glsl/test.frag', 'test2.frag': 'glsl/test2.frag', });