これはthree.jsでスプライトを生成して表示するためのコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>サーバー上でないと表示されません</title> </head> <body> <canvas id="canvas"></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script> <script> // ページが読み込まれたらinit()が実行される window.addEventListener('load', init); function init() { // サイズを指定 const width = 320; const height = 320; // レンダラーを作成 const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#canvas') }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); // シーンを作成 const scene = new THREE.Scene(); // カメラを作成 const camera = new THREE.PerspectiveCamera(45, width / height); camera.position.x = 0; camera.position.z = 15; camera.position.y = 0; camera.lookAt(new THREE.Vector3(0, 0, 0)); // 画像を読み込む let loader = new THREE.TextureLoader(); let texture = loader.load('1.png'); texture.needsUpdate = true; // マテリアルにテクスチャーを設定 const material = new THREE.SpriteMaterial({ map: texture }); const sprite = new THREE.Sprite(material); sprite.position.x = 0; sprite.position.y = 0; sprite.position.z = 0; sprite.scale.set(4,4,4); scene.add(sprite); tick(); // 毎フレーム時に実行されるループイベント function tick() { requestAnimationFrame(tick); // レンダリング renderer.render(scene, camera); } } </script> </body> </html> |
ここから動作確認ができます。サンプルコードはここからダウンロードできます。
さてダウンロードし解凍したフォルダ内にあるindex.htmlをダブルクリックして同じように表示されたでしょうか?
実はローカルHTMLでやっている場合(URLがfileスキーム)はファイルが読み込めない場合があるのです。だからといって動作確認をするためにその都度サーバーにアップロードするのは面倒です(ローカルサーバーを使うという方法もありますが・・・)。
画像をdataURLに変換してしまえばこの問題を回避することができます。
フォルダのなかにある1.pngをdataURLに変換します。すると
1 |
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAACcCAYAAACtFk (長々と続く) |
のようになります。これを使って最初のコードを書き直すと以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>これならローカルでも表示されます</title> </head> <body> <canvas id="canvas"></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script> <script> // ページの読み込みを待つ window.addEventListener('load', init); function init() { // サイズを指定 const width = 320; const height = 320; // レンダラーを作成 const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#canvas') }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); // シーンを作成 const scene = new THREE.Scene(); // カメラを作成 const camera = new THREE.PerspectiveCamera(45, width / height); camera.position.x = 0; camera.position.z = 15; camera.position.y = 0; camera.lookAt(new THREE.Vector3(0, 0, 0)); // この部分はやめる // let loader = new THREE.TextureLoader(); // let texture = loader.load('1.png'); // このように変更する let img = new Image(); img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUh(めっちゃ長いので省略)oQXMAAAAASUVORK5CYII="; let texture = new THREE.Texture(img); // 変更終わり // ここから下は同じ texture.needsUpdate = true; // マテリアルにテクスチャーを設定 const material = new THREE.SpriteMaterial({ map: texture }); const sprite = new THREE.Sprite(material); sprite.position.x = 0; sprite.position.y = 0; sprite.position.z = 0; sprite.scale.set(4,4,4); scene.add(sprite); tick(); // 毎フレーム時に実行されるループイベントです function tick() { requestAnimationFrame(tick); // レンダリング renderer.render(scene, camera); } } </script> </body> </html> |
サンプルコードはここからダウンロードできます。
ところでdata URLはどうやって生成すればいいのでしょうか? 生成するためのアプリをつくってみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_DragDrop(object sender, DragEventArgs e) { if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return; string[] vs = (string[])e.Data.GetData(DataFormats.FileDrop); string path = vs[0]; byte[] bytes = GetBytesFromFile(path); string mediatype = "image/png"; var info = new System.IO.FileInfo(path); if (info.Extension.ToLower() == ".png") mediatype = "image/png"; if (info.Extension.ToLower() == ".jpg" || info.Extension.ToLower() == ".jpeg") mediatype = "image/jpeg"; if (info.Extension.ToLower() == ".bmp") mediatype = "image/bmp"; if (info.Extension.ToLower() == ".gif") mediatype = "image/gif"; string str = String.Format("data:{0};base64,{1}", mediatype, Convert.ToBase64String(bytes)); richTextBox1.Text = str; } private void Form1_DragOver(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { e.Effect = DragDropEffects.Copy; } } byte[] GetBytesFromFile(string path) { var fs = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read); // ファイルサイズを取得する int fileSize = (int)fs.Length; // データを格納するための配列 byte[] buf = new byte[fileSize]; // Readメソッドで読み込んだバイト数 int readSize; // 読み込むべき残りのバイト数 int remain = fileSize; // データ格納用配列内の追加位置 int bufPos = 0; while (remain > 0) { // 1024Bytesずつ読み込む readSize = fs.Read(buf, bufPos, Math.Min(1024, remain)); bufPos += readSize; remain -= readSize; } fs.Dispose(); return buf; } private void ButtonCopy_Click(object sender, EventArgs e) { Clipboard.SetText(RichTextBox1.Text); } private void ButtonClear_Click(object sender, EventArgs e) { RichTextBox1.Clear(); } } |