誰向け?
GLSLのコンパイルエラーのエラーメッセージがブラウザのコンソールで見られず困っている人向け。
この記事ではp5.jsを使った例を上げますが、p5.jsを使っていない人にも考え方は役に立つはず。
解決したい問題
そもそもp5.jsはGLSLのコンパイルエラーのエラーメッセージをコンソールに表示する機能を備えています。
この画像内の
🌸 p5.js says: Snap! Error linking shader program: Precisions of uniform 'uRed' differ between VERTEX and FRAGMENT shaders.
という奴がそうです。
ところが私の環境だと何故かこれがコンソールに出なくなってしまいました。
Node.js
かReact
かNext.js
かTypeScript
あたりが怪しいのですが、調査しても原因特定できず。心当たりのある方は教えて下さい…。
ということで、下記の方法では、p5.jsの機能に頼らず、シェーダーのコンパイルエラーのエラーメッセージを取得します。
方法
Vertex Shader
とFragment Shader
と、それらを集約したWebGL Program
の3箇所にエラーが格納されています。これを読み出せばいいです。
Vertex Shader
とFragment Shader
のエラー
→ getShaderInfoLog
で取得
WebGL Program
のエラー
→ getProgramInfoLog
で取得
Vertex Shader
と Fragment Shader
については、WebGL2RenderingContext と WebGLShader のインスタンスを取得し、Context の getShaderInfoLog メソッドに Shader のインスタンスを渡せばエラーメッセージが取得できるので、それを console.log
などすればよいです。
p5.js の場合は、 p5.Shader の中にある vertexShader と fragmentShader のインスタンスを取り出して、Context の getShaderInfoLog に渡します。
WebGL Program
については、getProgramInfoLog メソッドを使って、ほぼ同様にします。
各シェーダー・プログラムのインスタンスのありか
https://github.com/processing/p5.js/blob/main/src/webgl/p5.Shader.js
こちらのソースコードを見ると、p5.Shader
は
_vertShader
_fragShader
_glProgram
というプロパティを持っていることがわかります。これらがそうです。
コード例
まずはHTML。p5.js
とscript.js
を読み込んでいるだけで、あとはテンプレ、ボイラープレートです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.8.0/p5.js"></script>
</head>
<body>
<script src="script.js"></script>
</body>
</html>
次の JavaScript
がこの記事のメインです!
p5.Shader
の中にある Vertex Shader
と Fragment Shader
と WebGL Program
をそれぞれ取り出して、メッセージが記録されていればそれをコンソールに throw
しています。
window.onload = function () {
const sketch = (p5) => {
let myShader; // p5.Shaderを格納
let myCanvas; // p5.Rendererを格納(canvasを指し示す)
let gl; // WebGL2RenderingContextを格納
p5.preload = () => {
// preload内でシェーダを読み込む
myShader = p5.loadShader('./shader.vert', './shader.frag');
}
p5.setup = () => {
myCanvas = p5.createCanvas(400, 400, p5.WEBGL); // 作ったCanvasを代入
gl = myCanvas.elt.getContext('webgl2'); // GL2のContextを取得して代入
p5.noStroke();
}
p5.draw = () => {
p5.background(0);
const red = [1.0, 0.0, 0.0];
const green = [0.0, 1.0, 0.0];
myShader.setUniform("uColors", [red, green].flat());
p5.shader(myShader);
// vertex shaderのコンパイルエラーを取得して表示
const messageVert = gl.getShaderInfoLog(myShader._vertShader);
if (messageVert !== null && messageVert.length > 0) {
console.log("error of vertex shader");
throw messageVert;
}
// fragment shaderのコンパイルエラーを取得して表示
const messageFrag = gl.getShaderInfoLog(myShader._fragShader);
if (messageFrag !== null && messageFrag.length > 0) {
console.log("error of fragment shader");
throw messageFrag;
}
// WebGLProgramのエラーを取得して表示
const messageProgram = gl.getProgramInfoLog(myShader._glProgram);
if (messageProgram !== null && messageProgram.length > 0) {
console.log("error of gl program");
throw messageProgram;
}
p5.beginShape(p5.TRIANGLES); // 三角形を描画
p5.vertex(0.0, 0.5); // gl_VertexID == 0
p5.vertex(-0.5, -0.5); // gl_VertexID == 1
p5.vertex(0.5, -0.5); // gl_VertexID == 2
p5.endShape();
}
}
new p5(sketch);
};
2つのシェーダーのGLSLコードは、この記事の内容的にはどうでもいいですが、後ほどこれを少し破壊して、どんなエラーが出るか確認してみます。
#version 300 es
precision mediump float;
uniform vec3 uColors[2];
in vec3 aPosition;
out vec4 vertColor;
void main() {
gl_Position = vec4(aPosition, 1.0);
// 1色目と2色目はuniformで受け取り、3色目はハードコードしています。
vertColor =
gl_VertexID == 0 ? vec4(uColors[0],1.0):
gl_VertexID == 1 ? vec4(uColors[1],1.0):
vec4(0.0, 0.0, 1.0, 1.0);
}
#version 300 es
precision mediump float;
in vec4 vertColor;
out vec4 fragColor;
void main() {
// vertex shaderから受け取った値を色として出力するだけ
fragColor = vertColor;
}
出力結果
エラー例1 vertex shaderのエラー
- out vec4 vertColor;
+ out vec3 vertColor;
vertex shader で型エラーを引き起こしてみます。
script.js:30 Uncaught ERROR: 0:12: '=' : dimension mismatch
ERROR: 0:12: 'assign' : cannot convert from 'mediump 4-component vector of float' to 'out mediump 3-component vector of float'
いいですね。fragment shader ではなく vertex shader が怒られている、ということもちゃんとわかります。
エラー例2 fragment shaderのエラー
- out vec4 fragColor;
こうすると…
script.js:37 Uncaught ERROR: 0:9: 'fragColor' : undeclared identifier
ERROR: 0:9: 'assign' : l-value required (can't modify a const)
ERROR: 0:9: '=' : dimension mismatch
ERROR: 0:9: 'assign' : cannot convert from 'in highp 4-component vector of float' to 'const highp float'
ちゃんと undeclared identifier
と怒られましたね。fragment shader
が怒られていることもちゃんとわかります。
エラー例3 vertex shaderとfragment shaderの連携エラー
昔の呼び方で varying
と呼ばれていた奴ですね。型を不一致にさせてみます。
- in vec4 vertColor;
- out vec4 fragColor;
+ in vec3 vertColor;
+ out vec3 fragColor;
Uncaught Types of varying 'vertColor' differ between VERTEX and FRAGMENT shaders.
FRAGMENT varying vertColor does not match any VERTEX varying
今回は WebGL Program
でエラーが発生したことがわかります。
まとめ
とにかくこれらを取得して、console.log
Vertex Shader
とFragment Shader
のエラー
→ getShaderInfoLog
で取得
WebGL Program
のエラー
→ getProgramInfoLog
で取得