GLSLを別ファイルで読み込む

three.jsでGLSLを書く場合、以下のように直接HTMLファイルに書き込むことになるが、コードの管理やシンタックスハイライトなどで不都合がでる。そこでGLSLファイルは別ファイルとして読み込みたい。

XMLHttpRequestを使用する

XMLHttpRequestを使用することで外部のテキストファイルを読み込むことができる。ただし、ローカルから読み込む場合Chromeの設定を変えたりする必要があるため面倒くさい。そこで、Visual Studio Codeの拡張機能であるLive Serverを使用するなどして、ローカルサーバーを立てるのがよい。

ファイルの同期読み込み ChromeではXMLHttpRequestを非同期でつかわないと警告を出されるが、非同期は難しいので無視する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// XMLHttpRequestを使用したファイルの読み込み
function loadFile(url, data){
    var request = new XMLHttpRequest();
    request.open('GET', url, false);

    request.send(null);

    // リクエストが完了したとき
    if(request.readyState == 4){
        // Http status 200 (成功)
        if(request.status == 200){
            return request.responseText;
        }else{ // 失敗
            console.log("error");
            return null;
        }
    }
}

シェーダーの読み込み

HTMLファイル

 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
<!DOCTYPE html>
<html>
    <head>
        <title>Shader.js app</title>
        <style>
            body { margin: 0; }
            canvas { width: 100%; height: 100% }
        </style>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@master/build/three.min.js"></script>
        <script>

            function loadFile(url, data){
                var request = new XMLHttpRequest();
                request.open('GET', url, false);

                request.send(null);

                // リクエストが完了したとき
                if(request.readyState == 4){
                    // Http status 200 (成功)
                    if(request.status == 200){
                        return request.responseText;
                    }else{ // 失敗
                        console.log("error");
                        return null;
                    }
                }
            }

            var scene = new THREE.Scene();
            var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

            //シェーダーの読み込み

            var vert = loadFile("vertex.vert");
            var frag = loadFile("fragment.frag");

            var renderer = new THREE.WebGLRenderer();
            renderer.setSize( window.innerWidth, window.innerHeight );
            document.body.appendChild( renderer.domElement );
            var uniforms = {
                "cubeColor": { value: new THREE.Vector3( 0, 0.5, 1 ) }
            };

            var geometry = new THREE.BoxGeometry( 1, 1, 1 );
            var material =  new THREE.ShaderMaterial( {
                    uniforms: uniforms,
                    vertexShader: vert,
                    fragmentShader: frag
                } );
            var cube = new THREE.Mesh( geometry, material );
            scene.add( cube );

            camera.position.z = 5;

            var animate = function () {
                requestAnimationFrame( animate );

                cube.rotation.x += 0.01;
                cube.rotation.y += 0.01;

                renderer.render( scene, camera );
            };

            animate();
        </script>
    </body>
</html>

頂点シェーダー(vertex.vert)

1
2
3
4
5
6
7
8
9
uniform vec2 uvScale;
varying vec2 vUv;

void main()
{
    vUv = uvScale * uv;
    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    gl_Position = projectionMatrix * mvPosition;
}

フラグメントシェーダー(fragment.frag)

1
2
3
4
5
6
uniform vec3 cubeColor;


void main( void ) {
    gl_FragColor = vec4( cubeColor,1.0);
}