どうも!かけちまるです!
Three.jsでシェーディングするために使うGLSL。
その中でも重要な修飾子の一つであるvarying
に注目して解説します。
varying
を理解することで、画像をゆらゆらアニメーションさせたり、色を動的に変えたりできます。
GLSLってなんなん?という方はこちらの記事が参考になりそうです。
この記事では、
がわかります。
varying
は、GLSLの修飾子のひとつです。
修飾子は他に、attribute
、uniform
があり、よく使われます。
それぞれの役割は次のような感じ
attribute
・・・頂点情報などを入れるuniform
・・・グローバル変数を入れるvarying
・・・VertexShaderからFragmentShaderに変数を渡すときに使う上記にあるようにvarying
はVertexShaderからFragmentShaderに変数を渡す役割があります。
では、実際にどのように使うのか見ていきましょう。
この記事では次のような成果物を作ります。
次のテンプレートをベースに実装していきます。
テンプレートをコピペして次のような感じになっていればOKです。
ディレクトリ構成index.html js/ -main.js
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script async src="https://unpkg.com/es-module-shims@1.5.8/dist/es-module-shims.js"></script> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.142.0/build/three.module.js" } } </script> <script src="./js/main.js" type="module"></script> <style> *{ margin: 0; padding: 0; box-sizing: border-box; } </style> </head> <body> <script id="v-shader" type="x-shader/x-vertex"> void main(){ gl_Position = projectionMatrix * modelMatrix * viewMatrix * vec4(position,1.0); } </script> <script id="f-shader" type="x-shader/x-fragment"> void main(){ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } </script> </body> </html>
main.jsimport * as THREE from 'three'; /** * シーン・カメラ・レンダラーを用意する */ const fov = 50; const fovRad = (fov / 2) * (Math.PI / 180); // 視野角をラジアンに変換 let distance = (window.innerHeight / 2) / Math.tan(fovRad); // カメラ距離を求める const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 0.1, 2000 ); camera.position.z = distance; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); /** * ジオメトリ+マテリアル=メッシュを用意する */ const planeGeometry = new THREE.PlaneGeometry(200, 200); const shaderMaterial = new THREE.ShaderMaterial({ //バーテックスシェーダを設定 vertexShader: document.querySelector('#v-shader').textContent, //フラグメントシェーダを設定 fragmentShader: document.querySelector('#f-shader').textContent, }); let mesh = new THREE.Mesh(planeGeometry, shaderMaterial); scene.add(mesh); // アニメーション function animate() { renderer.render(scene, camera); requestAnimationFrame(animate); } animate(); // ブラウザのリサイズに対応させる window.addEventListener('resize', onWindowResize); function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; distance = (window.innerHeight / 2) / Math.tan(fovRad); camera.position.z = distance; camera.updateProjectionMatrix(); }
テンプレート通りにやったけどうまくいかない場合はVisual Studio CodeのLive Serverというプラグインを使ってローカルサーバー環境で試してみてください。
※他のエディタを使っている方はローカルサーバーを構築できる方法がないか調べてみてください🙇♂️
ShaderMaterialの引数にuniforms
を設定
まず、VertexShaderに渡す値をShaderMaterialに設定します。
設定する値は引数uniforms
に設定します。
main.js・・・ /** * ジオメトリ+マテリアル=メッシュを用意する */ const planeGeometry = new THREE.PlaneGeometry(200, 200); const shaderMaterial = new THREE.ShaderMaterial({ uniforms: { uColor: { value: new THREE.Vector3(1.0, 0.0, 1.0) } }, //バーテックスシェーダを設定 vertexShader: document.querySelector('#v-shader').textContent, //フラグメントシェーダを設定 fragmentShader: document.querySelector('#f-shader').textContent, }); let mesh = new THREE.Mesh(planeGeometry, shaderMaterial); scene.add(mesh); ・・・
uniforms
の設定値をVertexShaderで受け取る
VertexShaderでuniform
修飾子を使い、値を受け取ります。
index.html・・・ <script id="v-shader" type="x-shader/x-vertex"> uniform vec3 uColor; void main(){ vColor = uColor; gl_Position = projectionMatrix * modelMatrix * viewMatrix * vec4(position,1.0); } </script> <script id="f-shader" type="x-shader/x-fragment"> void main(){ gl_FragColor = vec4(vColor, 1.0); } </script> ・・・
varying
修飾子でFragmentShaderに渡す
VertexShaderでvarying
修飾子で変数を用意し、main()
関数内で②で受け取った値を入れます。
これでVertexShaderからFragmentShaderに値を渡す準備ができました。
そして、FragmentShaderでもvarying
修飾子に受け取りたい変数名を定義します。
これで、FragmentShaderで値を受け取ることができます。
index.html・・・ <script id="v-shader" type="x-shader/x-vertex"> uniform vec3 uColor; varying vec3 vColor; void main(){ vColor = uColor; gl_Position = projectionMatrix * modelMatrix * viewMatrix * vec4(position,1.0); } </script> <script id="f-shader" type="x-shader/x-fragment"> varying vec3 vColor; void main(){ gl_FragColor = vec4(vColor, 1.0); } </script> ・・・
ここまでで色が変わっていればOKです。
実装確認
原始的ですが、main.jsのuColor
の値を変えると正方形の色が変わることがわかると思います。
これで、VertexShaderからFragmentShaderに値が渡されていることがわかりますね。
ちなみにGLSLでは色を0.0〜1.0
で表現します。
この記事で作ったサンプルでは、varying
修飾子の重要性はそれほど感じなかったかもしれません。
例えば、VertexShaderで座標毎に色を計算し、値を渡すことでグラデーションを表現できたりします。
VertexShaderでは座標ごとにひと手間加えたい事が多いのでvarying
修飾子は重要かなと思います。
おわり
フィードバックを送信
記事についてのフィードバックはTwitterかお問い合わせフォームから受け付けております。