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クレジット分無料でダウンロードすることができます。
※textures.comの無料クレジットは廃止になりました。
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で立方体にテクスチャを設定しました。

