Twitter
2021年04月17日 - Three.js・WebVR

平面にシェーダを設定

GLSLでレイマーチング」までWebGLとGLSLについていろいろと試してきましたが、またThree.jsでいろいろと制作したいと思い、平面にシェーダを設定してみました。※Three.jsはr127を使用しています。

平面にシェーダを設定

● 平面を生成

PlaneGeometryを使用して平面を生成します。

また、動作確認のため、MeshBasicMaterialを設定しました。

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);

	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 };

完成したデモになります。平面にシェーダを設定してみました。

  • このエントリーをはてなブックマークに追加

関連記事

前の記事へ

GLSLでレイマーチング

次の記事へ

RawShaderMaterialにライトを設定