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 };
完成したデモになります。平面にシェーダを設定しました。

