2021年04月17日 - WebVR・Three.js
平面にシェーダを設定
「GLSLでレイマーチング」までWebGLとGLSLについていろいろと試してきましたが、またThree.jsでいろいろと制作したいと思い、平面にシェーダを設定してみました。※Three.jsはr127を使用しています。
平面にシェーダを設定
● 平面を生成
const geometry = new THREE.PlaneGeometry(10,10,2,2); const material = new THREE.MeshBasicMaterial(); const plane = new THREE.Mesh(geometry,material); scene.add(plane);
● RawShaderMaterial
MeshBasicMaterialをRawShaderMaterialに変更してシェーダを設定します。RawShaderMaterialについては「RawShaderMaterial」を参考にしてください。また、projectionMatrixなどThree.jsに設定されているuniformsやattributesなどの組み込み変数はWebGLProgramで確認できます。
//バーテックスシェーダ const vertexShader =` //精度の指定を追加 precision mediump float; //positionの宣言 attribute vec3 position; //uvの宣言 attribute vec2 uv; //projectionMatrixの宣言 uniform mat4 projectionMatrix; //modelViewMatrixの宣言 uniform mat4 modelViewMatrix; varying vec2 vUv; void main(){ gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); //フラグメントシェーダにuvを転送 vUv = uv; } `; //フラグメントシェーダ const fragmentShader =` //精度の指定 precision mediump float; //timeを取得 uniform float time; //vUvを取得 varying vec2 vUv; void main(){ //uv座標系で、オブジェクトの中心に原点を設定 vec2 p = (vUv * 2.0 - 1.0); //ドットアニメーション p.x -= time * 0.00075; p *= 8.0; p = mod(p,3.0)-1.0; float l = length(p); l = step(0.0,1.0-l); gl_FragColor = vec4(l,l,0.0,1.0); } `; //timeを設定 const uniforms = { time:{type:'f',value:0.0} }; const geometry = new THREE.PlaneGeometry(10,10,2,2); //RawShaderMaterial const material = new THREE.RawShaderMaterial({ vertexShader:vertexShader, fragmentShader:fragmentShader, uniforms:uniforms, side:THREE.DoubleSide, }); const plane = new THREE.Mesh(geometry,material); scene.add(plane); let time = 0; function rendering(){ requestAnimationFrame(rendering); time++; //timeを更新 plane.material.uniforms.time.value = time; renderer.render(scene,camera); }
● script.js
必要なライブラリを読み込みます。
<script src="js/preloadjs.min.js"></script> <script src="js/TweenMax.min.js"></script> <script src="js/script.js" type="module"></script>
完成したscript.jsになります。
//=============================================================== // Import Library //=============================================================== import * as THREE from './lib/three_jsm/three.module.js'; import { OrbitControls } from './lib/three_jsm/OrbitControls.js'; import { scene, camera, container, renderer } from './lib/basescene.js'; import { vertexShader, fragmentShader } from './glsl.js'; //=============================================================== // Init //=============================================================== window.addEventListener('load',function(){ init(); }); let orbitControls; let plane; let time = 0; function init(){ setLoading(); } function setLoading(){ TweenMax.to('.loader',0.1,{opacity:1}); TweenMax.to('#loader_wrapper',1,{ opacity:0, delay:1, onComplete: function(){ document.getElementById('loader_wrapper').style.display = 'none'; TweenMax.to('.loader',0,{opacity:0}); } }); threeWorld(); setLight(); setControll(); rendering(); } //=============================================================== // Create World //=============================================================== function threeWorld(){ renderer.outputEncoding = THREE.sRGBEncoding; const gridHelper = new THREE.GridHelper(50,50); gridHelper.position.y = -5; scene.add(gridHelper); const uniforms = { time:{type:'f',value:0.0} }; const geometry = new THREE.PlaneGeometry(10,10,2,2); const material = new THREE.RawShaderMaterial({ vertexShader:vertexShader, fragmentShader:fragmentShader, uniforms:uniforms, side:THREE.DoubleSide, }); plane = new THREE.Mesh(geometry,material); scene.add(plane); } function setLight(){ const ambientlight = new THREE.AmbientLight(0xFFFFFF,1.0); scene.add(ambientlight); } function setControll(){ document.addEventListener('touchmove',function(e){e.preventDefault();},{passive:false}); orbitControls = new OrbitControls(camera,renderer.domElement); orbitControls.enableDamping = true; orbitControls.dampingFactor = 0.5; } function rendering(){ requestAnimationFrame(rendering); if(orbitControls){ orbitControls.update(); } time++; plane.material.uniforms.time.value = time; renderer.render(scene,camera); }
● basescene.js
sceneやcameraなど基本的な設定を管理するbasescene.jsです。
//=============================================================== // Import Library //=============================================================== import * as THREE from './three_jsm/three.module.js'; //=============================================================== // Base scene //=============================================================== let scene,camera,container,renderer; init(); function init(){ scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(50,window.innerWidth/window.innerHeight,1,1000); camera.position.set(0,1,25); scene.add(camera); renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth,window.innerHeight); renderer.setClearColor(new THREE.Color(0x000000)); container = document.querySelector('#canvas_vr'); container.appendChild(renderer.domElement); window.addEventListener('resize',function(){ camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth,window.innerHeight); },false); } export { scene, camera, container, renderer }
● glsl.js
シェーダを管理するglsl.jsです。
const vertexShader =` precision mediump float; attribute vec3 position; attribute vec2 uv; uniform mat4 projectionMatrix; uniform mat4 modelViewMatrix; varying vec2 vUv; void main(void){ vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } `; const fragmentShader =` precision mediump float; uniform float time; varying vec2 vUv; void main(void){ vec2 p = (vUv * 2.0 - 1.0); p.x -= time * 0.00075; p *= 8.0; p = mod(p,3.0)-1.0; float l = length(p); l = step(0.0,1.0-l); gl_FragColor = vec4(l,l,0.0,1.0); } `; export { vertexShader, fragmentShader };
完成したデモになります。平面にシェーダを設定してみました。