<div id="container">
  <div class="box">
    <div id="canvas">
      <canvas id="c">
      </canvas>
    </div>
  </div>
</div>
html,
body
  height 100%

#container
  width 100%
  height 100%
  background #d7d7d7
  /* --- FLEX --- */
  display flex
  justify-content center
  align-items center
  
.box
  width 200px
  height 200px
  background #fffafa
  color #fff
  text-align center
  box-shadow 5px 5px 20px #666
  display inherit
  justify-content inherit
  align-items inherit

#canvas
  width 100%
  height 100%
  
#c
  width 100%
  height 100%

View Compiled
// テーマ:シェーダーをGUIでちょうせいできるようにする
// お手本
// https://qiita.com/doxas/items/00567758621bb506e584

////////////////////////////////////////////////////////////////////////
// 必要なモジュール類
////////////////////////////////////////////////////////////////////////
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/build/three.module.js';
import { OrbitControls } from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/examples/jsm/controls/OrbitControls.js';

////////////////////////////////////////////////////////////////////////
// 本体
////////////////////////////////////////////////////////////////////////
main();

function main() {
  ////////////////////////////////////////////////////////////////////////
  // キャンバスの設定
  ////////////////////////////////////////////////////////////////////////
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});
  renderer.autoClearColor = false;
  
  ////////////////////////////////////////////////////////////////////////
  // 正射影カメラ正射影を使用するカメラです。
  //この投影モードでは、カメラからの距離にかかわらず、レンダリング画像内のオブジェクトのサイズが一定になります。
  ////////////////////////////////////////////////////////////////////////
  const left = -1;
  const right = 1;
  const top = 1;
  const buttom = -1;
  const near = -1;
  const far = 1;

  const camera = new THREE.OrthographicCamera(
    left,
    right,
    top,
    buttom,
    near,
    far
  );
  ////////////////////////////////////////////////////////////////////////
  // シーンの生成
  ////////////////////////////////////////////////////////////////////////
  const scene = new THREE.Scene();
  ////////////////////////////////////////////////////////////////////////
  // ジオメトリの生成 
  ////////////////////////////////////////////////////////////////////////
  const plane = new THREE.PlaneGeometry(2, 2);
  ////////////////////////////////////////////////////////////////////////
  // シェーダーの生成 
  ////////////////////////////////////////////////////////////////////////
  const fragmentShader = `
      #include <common>

      //iResolution: 解像度
      uniform vec3 iResolution;
      uniform float iTime;
      uniform float lineWidth;
      uniform float numberOfDivisions;
      // マウスの位置
      uniform vec2 u_mouse;
      
      // out 出力用 in 入力用
      void mainImage( out vec4 fragColor, in vec2 fragCoord ){
          float orbNumber = 100.0;
          float redColor = 1.0;
          float greenColor = 0.3;
          float buleColor = 0.7;
          float radius = 0.3;
          float pi = 3.14;
          float angle = pi / numberOfDivisions;
          
          //正規化(原点を中心におく)
          vec2 position = (fragCoord.xy * 2.0 - iResolution.xy) / min(iResolution.x, iResolution.y);
         
          vec3 destColor = vec3(redColor, greenColor, buleColor);
          float far = 0.0;
          
          // 1ループごとに円一個
          for(float i = 0.0; i < orbNumber; i++){
            float s = sin(iTime + i * angle) * 0.5;
            float c = cos(iTime + i * angle) * 0.5;
            // fの値を 原点から 一定の距離にある場所ほど小さい値にする
            // →計算結果が 0.0 に近づくようにする
            // →abs(length(position + vec2(c, s)) - 0.5) を 0.0025に近づけると 1になる 1に近づくと明るい
            far += lineWidth / abs(length(position + vec2(c, s)) - radius - sin(u_mouse.y));
          }

          // 画面への出力
          fragColor = vec4(vec3(destColor * far), 1.0);
      }

      void main() {
        mainImage(gl_FragColor, gl_FragCoord.xy);
      }
  `;
 const uniforms = {
    iTime: { value: 0 },
    lineWidth: { value: 0.0030 },
    numberOfDivisions: { value: 10.0 },
    iResolution:  { value: new THREE.Vector3() },
    u_mouse: { value: new THREE.Vector2() }
  };
  ////////////////////////////////////////////////////////////////////////
  // インタラクティブ(マウス)
  ////////////////////////////////////////////////////////////////////////
  document.onmousemove = function(e){
     uniforms.u_mouse.value.x = e.pageX
     uniforms.u_mouse.value.y = e.pageY
     console.log(uniforms.u_mouse.value.x,uniforms.u_mouse.value.y)
  }
  
  ////////////////////////////////////////////////////////////////////////
  // マテリアルの生成 
  ////////////////////////////////////////////////////////////////////////
   //ShaderMaterial:カスタムシェーダーでレンダリングされたマテリアルです。
  //シェーダーとは、GPU上で動作するGLSLで書かれた小さなプログラムです。
  const material = new THREE.ShaderMaterial({
    fragmentShader,
    uniforms,
  });
  scene.add(new THREE.Mesh(plane, material));
  
  
  ////////////////////////////////////////////////////////////////////////
  // OrbitControls
  // ref
  // https://www.i-ryo.com/entry/2020/05/02/215245#OrbitControlsjs%E3%82%92%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%80
  ////////////////////////////////////////////////////////////////////////
	const controls = new OrbitControls( camera, renderer.domElement );
	controls.minDistance = 0;
	controls.maxDistance = 100;
  
  ////////////////////////////////////////////////////////////////////////
  // GUI
  ////////////////////////////////////////////////////////////////////////
  const gui = new dat.GUI();
  ////////////////////////////
  //   シェーダー系
  ////////////////////////////
    {
    var originalShaderGuiControls = new function (){
      this.uniformsLineWidth = 0.0025;
      this.uniformsNumberOfDivisions = 1.0;
      this.onChange = function () {
        uniforms.lineWidth.value = originalShaderGuiControls.uniformsLineWidth;
        uniforms.numberOfDivisions.value = originalShaderGuiControls.uniformsNumberOfDivisions;
        
      }
    }
    {
      const folder = gui.addFolder('オリジナルシェーダー');
      folder.add(originalShaderGuiControls, 'uniformsLineWidth', 0.0025, 0.01)
        .name('ラインの太さ')
        .onChange(originalShaderGuiControls.onChange);
      
      folder.add(originalShaderGuiControls, 'uniformsNumberOfDivisions', 1, 10.0)
        .name('円を分割する数')
        .onChange(originalShaderGuiControls.onChange);
      folder.open();
    }
  }

  
  ////////////////////////////////////////////////////////////////////////
  // アニメーション
  //////////////////////////////////////////////////////////////////////// 
  function render(time) {
    time *= 0.001;  // convert to seconds
    resizeRendererToDisplaySize(renderer);
    const canvas = renderer.domElement;
    uniforms.iResolution.value.set(canvas.width, canvas.height, 1);
    uniforms.iTime.value = time;

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
  
  ////////////////////////////////////////////////////////////////////////
  // render関数内 ディスプレイサイズにリサイズ
  ////////////////////////////////////////////////////////////////////////
  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }
}


Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.min.js
  2. https://cdn.jsdelivr.net/npm/three@0.101.1/examples/js/controls/OrbitControls.js
  3. https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js
  4. https://threejsfundamentals.org/threejs/resources/threejs/r127/examples/jsm/postprocessing/EffectComposer.js