何をしたかったのか
canvasに描画したアニメーションをThree.jsで読み込みたいと思いました。MeshBasicMaterialを使えば、できるのですが、それだと、canvas一つにつき、一つのTextureしか作れないので、canvasに複数描画をして位置で分割して利用をしたらいいと思ったのですが、Three.jsでspritesheetを利用する方法がそこまでリファレンスがないので自分で作らないといけないので、shadermaterialを使ってglslを自分で書く必要がありました。なので、shadermaterialを使うところまで書きました。
実装した内容
qiita.rb
window.addEventListener('load', init);
function init() {
// サイズを指定
var width = 960;
var height = 540;
// レンダラーを作成
var renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('#myCanvas')
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
// シーンを作成
var scene = new THREE.Scene();
// カメラを作成
var camera = new THREE.PerspectiveCamera(45, width / height,0.1,10000);
camera.position.set(0, 0, +1000);
// 箱を作成
var geometry = new THREE.BoxGeometry(400, 400, 400);
var material = new THREE.ShaderMaterial({
vertexShader: document.getElementById('vs').textContent,
fragmentShader: document.getElementById('fs').textContent,
transparent: true,
depthWrite: true,
uniforms: {
"map": {
type: 't',
value: SOURCE.drawingAnim()
},
},
side:THREE.DoubleSide
});
/*
= new THREE.MeshBasicMaterial({
transparent: true,
map: drawingAnim(),
side:THREE.DoubleSide
});*/
//= new THREE.MeshNormalMaterial();
//document.getElementById('drawCanvas');
const box = new THREE.Mesh(geometry, material);
//box.material.map.needsUpdate = true;
box.doubleSided = true;
scene.add(box);
tick();
// 毎フレーム時に実行されるループイベントです
var count = 0;
function tick() {
count++;
SOURCE.next();
//box.material.map = drawingAnim();
box.material.uniforms.map.value.needsUpdate = true;
//box.material.uniforms.map = drawingAnim();
box.rotation.x += 0.01;
box.position.z = 200*Math.sin(0.1*count);
box.rotation.y += 0.01;
box.rotation.z += 0.01;
renderer.render(scene, camera); // レンダリング
requestAnimationFrame(tick);
}
}
htmlの方は
qiita.rb
<canvas id="myCanvas"></canvas>
<!-- drawCanvasの方をmyの方の素材として利用をする。 -->
<canvas id="drawCanvas" width="512" height="512"></canvas>
こんな感じでそして、canvasへの描画は
qiita.rb
var SOURCE = {
init:function(){
},
cnum:0,
ctx:null,
canvas:null,
drawingAnim:function(){
this.cnum++;
this.canvas = document.getElementById('drawCanvas');
if ( ! this.canvas || ! this.canvas.getContext ) { return false; }
this.ctx = this.canvas.getContext('2d');
this.ctx.clearRect(0, 0, 512, 512)
this.ctx.beginPath();
//this.ctx.fillStyle = 'rgb(255, 0, 0)'; // 赤
//this.ctx.fillRect(50, 50, 400, 400);
this.ctx.fillStyle = 'rgb(144, 187, 89)'; // 緑
this.ctx.fillRect(0, 0, 200+200*Math.sin(0.1*this.cnum), 200+200*Math.sin(0.01*this.cnum));
return new THREE.Texture(this.canvas);
},
next:function(){
this.cnum++;
this.ctx = this.canvas.getContext('2d');
this.ctx.clearRect(0, 0, 512, 512)
this.ctx.beginPath();
this.ctx.fillStyle = 'rgb(144, 187, 89)'; // 緑
this.ctx.fillRect(0, 0, 200+200*Math.sin(0.1*this.cnum), 200+200*Math.sin(0.01*this.cnum));
this.ctx.fillStyle = 'rgb(255, 0, 0)'; // 赤
this.ctx.fillRect(0, 0, 200, 200);
}
}
こんな感じにした。
glsl部分は
qiita.rb
<script id="vs" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fs" type="x-shader/x-fragment">
uniform sampler2D map;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D( map, vUv );
}
</script>
これで立方体だが表面がうにょうにょ動くのが完成しました