Twitter
2021年01月30日 - Three.js・WebVR

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で立方体にテクスチャを設定しました。

  • このエントリーをはてなブックマークに追加

関連記事

前の記事へ

WebGLでクォータニオン

次の記事へ

WebGLでトゥーンレンダリング