2021年01月30日 - WebVR・Three.js
WebGLでテクスチャ
「WebGLでクォータニオン」に続き「テクスチャマッピング-wgld.org」を参考に、WebGLで立方体にテクスチャを設定しました。
WebGLでテクスチャの設定
テクスチャを設定するには、テクスチャオブジェクトを生成してWebGLにバインドします。その後、テクスチャオブジェクトをシェーダに送り、シェーダでテクスチャ描画します。
テクスチャ画像はGIFやJPG、PNGなどが使用できます。ただし、画像の大きさを512px×512pxなど2の累乗にする必要があります。※WebGL2.0では2の累乗以外のサイズも使用できます。
● textures.com
今回、テクスチャ画像はtextures.comからダウンロードしました。
textures.comは登録すると、解像度の低いものだけですが、1日15クレジット分無料でダウンロードすることができます。
WebGLでテクスチャ
● 行列演算用ライブラリ
「minMatrixb.js リファレンス-wgld.org」を参考に、行列演算用ライブラリを読み込みます。minMatrixb.jsには立方体を生成する関数が実装されています。
<script src="js/lib/minMatrixb.js"></script> <script src="js/script.js" type="module"></script>
● script.js
//=============================================================== // GLSL //=============================================================== const vertexShader =` attribute vec3 position; attribute vec4 color; attribute vec2 textureCoord; uniform mat4 mvpMatrix; varying vec4 vColor; varying vec2 vTextureCoord; void main(void){ vColor = color; //テクスチャ座標をフラグメントシェーダに転送 vTextureCoord = textureCoord; gl_Position = mvpMatrix * vec4(position,1.0); } `; const fragmentShader =` precision mediump float; //テクスチャオブジェクトを取得 uniform sampler2D texture; varying vec4 vColor; //テクスチャ座標を取得 varying vec2 vTextureCoord; void main(void){ //テクスチャの設定 vec4 smpColor = texture2D(texture,vTextureCoord); gl_FragColor = vColor * smpColor; } `; //=============================================================== // Init & Redering //=============================================================== window.addEventListener('load',function(){ init(); rendering(); }); let canvas,gl; let m,mMatrix,vMatrix,pMatrix,vpMatrix,mvpMatrix; let prg,attLocation,attStride; let uniLocation; let cubeData,cVBOList,cIndex; let q,qt; let texture = null; function init(){ canvas = document.getElementById('webgl-canvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; gl = canvas.getContext('webgl'); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.enable(gl.CULL_FACE); let vShader = createShader(gl.VERTEX_SHADER,vertexShader); let fShader = createShader(gl.FRAGMENT_SHADER,fragmentShader); prg = createProgram(vShader,fShader); //attributeLocationの取得 attLocation = []; attLocation[0] = gl.getAttribLocation(prg,'position'); attLocation[1] = gl.getAttribLocation(prg,'color'); //立方体のテクスチャ座標 attLocation[2] = gl.getAttribLocation(prg,'textureCoord'); //attributeの要素数 attStride = []; attStride[0] = 3; attStride[1] = 4; attStride[2] = 2; //立方体の生成 cubeData = cube(2.0,[1.0,1.0,1.0,1.0]); const cPosition = createVbo(cubeData.p); const cColor = createVbo(cubeData.c); //立方体のテクスチャ座標 const cTextureCoord = createVbo(cubeData.t); cVBOList = [cPosition,cColor,cTextureCoord]; cIndex = createIbo(cubeData.i); //uniformLocationの取得 uniLocation = []; uniLocation[0] = gl.getUniformLocation(prg,'mvpMatrix'); //テクスチャ uniLocation[1] = gl.getUniformLocation(prg,'texture'); m = new matIV(); mMatrix = m.identity(m.create()); vMatrix = m.identity(m.create()); pMatrix = m.identity(m.create()); vpMatrix = m.identity(m.create()); mvpMatrix = m.identity(m.create()); q = new qtnIV(); qt = q.identity(q.create()); //テクスチャ画像の読み込み createTexture('./img/texture.jpg'); window.addEventListener('mousemove',mouseMove); } //アニメーション function rendering(){ gl.clearColor(0.0,0.0,0.0,1.0); gl.clearDepth(1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); const eyePosition = [0.0,0.0,10.0]; const camUp = []; q.toVecIII([0.0,0.0,10.0],qt,eyePosition); q.toVecIII([0.0,1.0,0.0],qt,camUp); m.lookAt(eyePosition,[0,0,0],camUp,vMatrix); m.perspective(50,canvas.width/canvas.height,0.1,100,pMatrix); m.multiply(pMatrix,vMatrix,vpMatrix); if(texture){ //テクスチャオブジェクトを生成 gl.activeTexture(gl.TEXTURE0); //テクスチャオブジェクトをバインド gl.bindTexture(gl.TEXTURE_2D,texture); setAttribute(cVBOList,attLocation,attStride); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,cIndex); m.identity(mMatrix); m.multiply(vpMatrix,mMatrix,mvpMatrix); gl.uniformMatrix4fv(uniLocation[0],false,mvpMatrix); gl.uniform1i(uniLocation[1],0); gl.drawElements(gl.TRIANGLES,cubeData.i.length,gl.UNSIGNED_SHORT,0); } gl.flush(); requestAnimationFrame(rendering); } //=============================================================== // Controll //=============================================================== function mouseMove(event){ const wh = 1 / Math.sqrt(canvas.width * canvas.width + canvas.height * canvas.height); let x = event.clientX - canvas.offsetLeft - canvas.width * 0.5; let y = event.clientY - canvas.offsetTop - canvas.height * 0.5; let sq = Math.sqrt(x * x + y * y); const r = sq * 2.0 * Math.PI * wh; if(sq != 1){ sq = 1 / sq; x *= sq; y *= sq; } if(q){ q.rotate(r,[y,x,0.0],qt); } } //=============================================================== // Function //=============================================================== function createShader(shaderType,shaderText){ const shader = gl.createShader(shaderType); gl.shaderSource(shader,shaderText); gl.compileShader(shader); return shader; } function createProgram(vs,fs){ const program = gl.createProgram(); gl.attachShader(program, vs); gl.attachShader(program, fs); gl.linkProgram(program); gl.useProgram(program); return program; } function createVbo(data){ const vbo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,vbo); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(data),gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER,null); return vbo; } function createIbo(data){ const ibo = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,ibo); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,new Int16Array(data),gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,null); return ibo; } function setAttribute(vbo,attL,attS){ for(let i in vbo){ gl.bindBuffer(gl.ARRAY_BUFFER,vbo[i]); gl.enableVertexAttribArray(attL[i]); gl.vertexAttribPointer(attL[i],attS[i],gl.FLOAT,false,0,0); } } //テクスチャを設定 function createTexture(source){ const img = new Image(); //画像を読み込み後、テクスチャを設定 img.onload = function(){ //テクスチャオブジェクトの生成 const tex = gl.createTexture(); //テクスチャオブジェクトをバインド gl.bindTexture(gl.TEXTURE_2D, tex); //画像を設定 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); //ミットマップを生成 gl.generateMipmap(gl.TEXTURE_2D); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); texture = tex; //テクスチャオブジェクトのバインドを無効化 gl.bindTexture(gl.TEXTURE_2D, null); }; img.src = source; }
完成したデモになります。WebGLで立方体にテクスチャを設定しました。