WebGLのtexImage2Dを実行するとエラーが発生する
JavaScriptで、画像に任意のフラグメントシェーダの処理を行うクラスを作成しています。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<canvas id="sheet"></canvas>
<canvas id="gl"></canvas>
<input type="file" id="image">
<script>
class Filter{
constructor(shader){
this.canvas=document.querySelector("#gl");
this.canvas.width=512;
this.canvas.height=512;
this.gl=this.canvas.getContext("webgl");
this.gl.viewport(0,0,512,512);
this.gl.clearColor(0,0,0,1);
let prog=this.gl.createProgram();
let vs=this.gl.createShader(this.gl.VERTEX_SHADER);
this.gl.shaderSource(vs,`
attribute vec2 position;
varying vec2 vTexCoord;
void main(){
vTexCoord = (position + 1.0).xy / 2.0;
gl_Position = vec4(position,0.0,1.0);
}
`);
this.gl.compileShader(vs);
if (!this.gl.getShaderParameter(vs, this.gl.COMPILE_STATUS)) {
console.log(this.gl.getShaderInfoLog(vs));
}
this.gl.attachShader(prog,vs);
let fs=this.gl.createShader(this.gl.FRAGMENT_SHADER);
this.gl.shaderSource(fs,shader);
this.gl.compileShader(fs);
this.gl.attachShader(prog,fs);
this.gl.linkProgram(prog);
if (!this.gl.getProgramParameter(prog, this.gl.LINK_STATUS)) {
console.log(this.gl.getProgramInfoLog(prog));
}
this.prog=prog;
}
draw(canvas,option={}){
this.gl.useProgram(this.prog);
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
let vertexPosAttr=this.gl.getAttribLocation(this.prog,"position");
let vertexBuf=this.gl.createBuffer();
let texture=this.gl.createTexture();
if(option.uniform){
Object.keys(option.uniform).forEach((k)=>{
let l=this.gl.getUniformLocation(this.prog,k);
switch(option.uniform[k].length){
case 1:
this.gl.uniform1f(l,option.uniform[k]);
break;
case 2:
this.gl.uniform2f(l,option.uniform[k]);
break;
case 3:
this.gl.uniform3f(l,option.uniform[k]);
break;
case 4:
this.gl.uniform4f(l,option.uniform[k]);
break;
}
});
}
this.gl.bindTexture(this.gl.TEXTURE_2D,texture);
this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,canvas);
//this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,1,1,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,new Uint8Array([255,0,0,255]));
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
this.gl.bindTexture(this.gl.TEXTURE_2D,null);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER,vertexBuf);
this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array([
1.0,1.0,
-1.0,1.0,
-1.0,-1.0,
1.0,-1.0
]),this.gl.STATIC_DRAW);
this.gl.vertexAttribPointer(vertexPosAttr,3,this.gl.FLOAT,false,0,0);
this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4);
return this.canvas;
}
}
let sepire=new Filter(`
precision mediump float;
varying vec2 vTexCoord;
uniform sampler2D texture;
void main(void){
vec4 color=texture2D(texture,vTexCoord);
float g=color.r*0.03+color.g*0.59+color.b*0.11;
gl_FragColor=vec4(g*0.94,g*0.78,g*0.57,color.a);
}
`);
let image=new Image();
image.onload=()=>{
let canvas=document.querySelector("#sheet");
let ctx=canvas.getContext("2d");
canvas.width=512;
canvas.height=512;
ctx.drawImage(image,0,0,512,512,0,0,512,512);
let s=sepire.draw(canvas);
}
document.querySelector("#image").onchange=(e)=>{
image.src=URL.createObjectURL(e.target.files[0]);
}
</script>
</body>
</html>
画像ファイルを参照し、ロードが完了するとcanvasに描画され、canvasの内容がWebGLのテクスチャに登録されてシェーダープログラムによってセピア加工され描画されるという動作を期待しているのですが、次のようなエラーが発生します。
RENDER WARNING: there is no texture bound to the unit 0
調べてみるとテクスチャ用の画像のロードが完了していないときに発生するエラーということなのですが、canvasではなくonload発生時の画像を引数に与えても、1x1のArrayBufferの色情報を与えても同じエラーが発生しました。原因はどこでしょうか。
WebGL2.0では対応ブラウザが少ないので、WebGL1.0の範囲内で対応したいと考えています。