Twitter
2021年06月26日 - WebVR・Three.js

シェーダで雲を制作

How to Create Procedural Clouds Using Three.js Sprites」を参考に、シェーダで雲を制作しました。※Three.jsはr129を使用しています。

シェーダで雲を制作

まずは平面を生成し、フラグメントシェーダで雲のテクスチャと形状を制作します。

● 平面を生成

PlaneGeometryにRawShaderMaterialを設定して、平面を生成します。RawShaderMaterialについては、「RawShaderMaterial」を参考にしてください。

const geometry = new THREE.PlaneGeometry(7,7,2,2);
const material = new THREE.RawShaderMaterial({
	vertexShader:vertexShader,
	fragmentShader:fragmentShader,
	side:THREE.DoubleSide,
	transparent:true
});
const plane = new THREE.Mesh(geometry,material);
scene.add(plane);

● 雲のテクスチャを設定

テクスチャはシェーダでも生成できますが、GPUへの負担を軽減するため、2枚のテクスチャ画像をスライドアニメーションさせて実装します。

テクスチャ画像は、「How to Create Procedural Clouds Using Three.js Sprites」の「Download Source」ボタンから取得できます。

let time = 0;

//テクスチャ画像の読み込み
const uTexture1 = new THREE.TextureLoader().load( './img/texture.jpg' );
const uniforms = {
	time:{type:'f',value:0.0},
	uTexture1:{type:'t',value:null},
};

//平面の生成
const geometry = new THREE.PlaneGeometry(7,7,2,2);
const material = new THREE.RawShaderMaterial({
	vertexShader:vertexShader,
	fragmentShader:fragmentShader,
	uniforms:uniforms
	side:THREE.BackSide,
	transparent:true
});

//テクスチャの設定
uniforms.uTexture1.value = uTexture1;

//テクスチャの繰り返し設定
uniforms.uTexture1.value.wrapS = uniforms.uTexture1.value.wrapT = THREE.RepeatWrapping;
plane = new THREE.Mesh(geometry,material);
scene.add(plane);

//アニメーション
function rendering(){
	requestAnimationFrame(rendering);
 
	time++;
	plane.material.uniforms.time.value = time;
	renderer.render(scene,camera);
}

● glsl.js

フラグメントシェーダで、2枚のテクスチャ画像をスライドアニメーションさせます。

//バーテックスシェーダ
const vertexShader =`
	precision highp float;

	attribute vec3 position;
	attribute vec2 uv;
	uniform mat4 projectionMatrix;
	uniform mat4 modelViewMatrix;

	varying vec2 vUv;

	void main(void){

		//フラグメントシェーダにuvを転送
		vUv = uv;

		vec4 mvPosition = modelViewMatrix * vec4(position,1.0);
		gl_Position = projectionMatrix * mvPosition;
	}
`;

//フラグメントシェーダ
const fragmentShader =`
	precision highp float;

	//テクスチャの取得
	uniform sampler2D uTexture1;
	uniform float time;

	varying vec2 vUv;

	//レベル補正でコントラストを調整
	vec4 gammaCorrect(vec4 color, float gamma){
		return pow(color, vec4(1.0 / gamma));
	}
	vec4 levelRange(vec4 color, float minInput, float maxInput){
		return min(max(color - vec4(minInput), vec4(0.0)) / (vec4(maxInput) - vec4(minInput)), vec4(1.0));
	}
	vec4 levels(vec4 color, float minInput, float gamma, float maxInput){
		return gammaCorrect(levelRange(color, minInput, maxInput), gamma);
	}

	void main(void){

		//テクスチャをスライドアニメーション
		vec4 txtNoise1 = texture2D(uTexture1,vec2(vUv.x + time * 0.0001,vUv.y - time * 0.00014));
		vec4 txtNoise2 = texture2D(uTexture1,vec2(vUv.x - time * 0.00002,vUv.y + time * 0.000017 + 0.2));

		//レベル補正で透明度を計算
		float alpha = levels((txtNoise1 + txtNoise2) * 0.6,0.2,0.4,0.7).r;

		l_FragColor = vec4(vec3(0.95,0.95,0.95),alpha);
	}
`;

export { vertexShader, fragmentShader };

● 雲の形状を設定

フラグメントシェーダで、テクスチャ画像を使用して雲の形状にマスクします。テクスチャ画像は、「How to Create Procedural Clouds Using Three.js Sprites」の「Download Source」ボタンから取得できます。

const fragmentShader =`
	precision highp float;

	uniform sampler2D uTexture1;

	//テクスチャの取得
	uniform sampler2D uTexture2;
	uniform float time;

	varying vec2 vUv;

	vec4 gammaCorrect(vec4 color, float gamma){
		return pow(color, vec4(1.0 / gamma));
	}
	vec4 levelRange(vec4 color, float minInput, float maxInput){
		return min(max(color - vec4(minInput), vec4(0.0)) / (vec4(maxInput) - vec4(minInput)), vec4(1.0));
	}
	vec4 levels(vec4 color, float minInput, float gamma, float maxInput){
		return gammaCorrect(levelRange(color, minInput, maxInput), gamma);
	}

	void main(void){
		vec4 txtNoise1 = texture2D(uTexture1,vec2(vUv.x + time * 0.0001,vUv.y - time * 0.00014));
		vec4 txtNoise2 = texture2D(uTexture1,vec2(vUv.x - time * 0.00002,vUv.y + time * 0.000017 + 0.2));

		//雲の形状のテクスチャ
		vec4 txtShape = texture2D(uTexture2,vUv);

		float alpha = levels((txtNoise1 + txtNoise2) * 0.6,0.2,0.4,0.7).r;

		//雲の形状にマスク
		alpha *= txtShape.r;

		l_FragColor = vec4(vec3(0.95,0.95,0.95),alpha);
	}
`;

● シンプレックスノイズと非整数ブラウン運動

シンプレックスノイズと非整数ブラウン運動(Fractional Brownian motion)で、雲の形状をモーフィングアニメーションさせ、蒸気境界効果を与えます。シンプレックスノイズに関しては、「GLSLでシンプレックスノイズ」を参考にしてください。

const fragmentShader =`
	precision highp float;

	uniform sampler2D uTexture1;
	uniform sampler2D uTexture2;
	uniform float time;
	uniform float uFac1;
	uniform float uFac2;
	uniform float uTimeFactor1;
	uniform float uTimeFactor2;
	uniform float uDisplStrength1;
	uniform float uDisplStrength2;

	varying vec2 vUv;

	// webgl-noise

	// Description : Array and textureless GLSL 2D/3D/4D simplex noise functions.
	//      Author : Ian McEwan,Ashima Arts.
	//  Maintainer : stegu
	//     Lastmod : 20201014(stegu)
	//     License : Copyright(C)2011 Ashima Arts.All rights reserved.
	//               Distributed under the MIT License.See LICENSE file.
	//               https://github.com/ashima/webgl-noise
	//               https://github.com/stegu/webgl-noise

	vec3 mod289(vec3 x){ return x - floor(x * (1.0 / 289.0)) * 289.0; }
	vec4 mod289(vec4 x){ return x - floor(x * (1.0 / 289.0)) * 289.0; }
	vec4 permute(vec4 x){ return mod289(((x*34.0)+1.0)*x); }
	vec4 taylorInvSqrt(vec4 r){ return 1.79284291400159 - 0.85373472095314 * r; }

	float snoise(vec3 v){
		const vec2 C = vec2(1.0/6.0, 1.0/3.0);
		const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
		vec3 i  = floor(v + dot(v, C.yyy) );
		vec3 x0 = v - i + dot(i, C.xxx) ;
		vec3 g = step(x0.yzx, x0.xyz);
		vec3 l = 1.0 - g;
		vec3 i1 = min( g.xyz, l.zxy );
		vec3 i2 = max( g.xyz, l.zxy );
		vec3 x1 = x0 - i1 + C.xxx;
		vec3 x2 = x0 - i2 + C.yyy;
		vec3 x3 = x0 - D.yyy;
		i = mod289(i);
		vec4 p = permute(permute(permute(
			i.z + vec4(0.0, i1.z, i2.z, 1.0))
			+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
			+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
		float n_ = 0.142857142857;
		vec3 ns = n_ * D.wyz - D.xzx;
		vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
		vec4 x_ = floor(j * ns.z);
		vec4 y_ = floor(j - 7.0 * x_);
		vec4 x = x_ * ns.x + ns.yyyy;
		vec4 y = y_ * ns.x + ns.yyyy;
		vec4 h = 1.0 - abs(x) - abs(y);
		vec4 b0 = vec4(x.xy, y.xy);
		vec4 b1 = vec4(x.zw, y.zw);
		vec4 s0 = floor(b0)*2.0 + 1.0;
		vec4 s1 = floor(b1)*2.0 + 1.0;
		vec4 sh = -step(h, vec4(0.0));
		vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
		vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
		vec3 p0 = vec3(a0.xy,h.x);
		vec3 p1 = vec3(a0.zw,h.y);
		vec3 p2 = vec3(a1.xy,h.z);
		vec3 p3 = vec3(a1.zw,h.w);
		vec4 norm = taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2, p2),dot(p3,p3)));
		p0 *= norm.x;
		p1 *= norm.y;
		p2 *= norm.z;
	        p3 *= norm.w;
		vec4 m = max(0.5 - vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0);
		m = m * m;
		return 105.0 * dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3)));
	}

	//非整数ブラウン運動
	float fbm3d(vec3 x, const in int it) {
		float v = 0.0;
		float a = 0.5;
		vec3 shift = vec3(100);

		for (int i = 0; i < 32; ++i) {
			if(i < it) {
				v += a * snoise(x);
				x = x * 2.0 + shift;
				a *= 0.5;
			}
		}
		return v;
	}

	vec4 gammaCorrect(vec4 color, float gamma){
		return pow(color, vec4(1.0 / gamma));
	}
	vec4 levelRange(vec4 color, float minInput, float maxInput){
		return min(max(color - vec4(minInput), vec4(0.0)) / (vec4(maxInput) - vec4(minInput)), vec4(1.0));
	}
	vec4 levels(vec4 color, float minInput, float gamma, float maxInput){
		return gammaCorrect(levelRange(color, minInput, maxInput), gamma);
	}

    void main(void){
    	vec2 newUv = vUv;

    	vec4 txtNoise1 = texture2D(uTexture1,vec2(vUv.x + time * 0.0001,vUv.y - time * 0.00014));
    	vec4 txtNoise2 = texture2D(uTexture1,vec2(vUv.x - time * 0.00002,vUv.y + time * 0.000017 + 0.2));

    	//蒸気境界効果を与える
    	float noiseBig = fbm3d(vec3(vUv * uFac1,time * uTimeFactor1),4) + 1.0 * 0.5;
    	newUv += noiseBig * uDisplStrength1;

    	//雲の形状をモーフィングアニメーションさせる
    	float noiseSmall = snoise(vec3(newUv * uFac2,time * uTimeFactor2)) + 1.0 * 0.5;
    	newUv += noiseSmall * uDisplStrength2;

    	vec4 txtShape = texture2D(uTexture2,newUv);
    	float alpha = levels((txtNoise1 + txtNoise2) * 0.6,0.2,0.4,0.7).r;
    	alpha *= txtShape.r;

		gl_FragColor = vec4(vec3(0.95,0.95,0.95),alpha);
	}
`;

● ビルボード効果の設定

スプライトマテリアルを拡張してビルボード効果を与え、雲が常にカメラに向くようにします。

const material = new THREE.RawShaderMaterial({
	vertexShader:vertexShader,
	fragmentShader:fragmentShader,
	uniforms: {

		//スプライトのuniforms
		...THREE.UniformsUtils.clone(THREE.ShaderLib.sprite.uniforms),
		...uniforms
    	},
	side:THREE.BackSide,
	transparent:true
});

バーテックスシェーダでビルボード効果を設定します。

const vertexShader =`
	precision highp float;

	attribute vec3 position;
    attribute vec2 uv;

    uniform float rotation;
    uniform vec2 center;
    uniform mat4 modelMatrix;
    uniform mat4 modelViewMatrix;
	uniform mat4 projectionMatrix;

	varying vec2 vUv;

	#include <common>
	#include <uv_pars_vertex>
	#include <fog_pars_vertex>
	#include <logdepthbuf_pars_vertex>
	#include <clipping_planes_pars_vertex>

	void main(void){
		vUv = uv;

		vec4 mvPosition = modelViewMatrix * vec4(0.0,0.0,0.0,1.0);
		vec2 scale;

		scale.x = length(vec3(modelMatrix[0].x,modelMatrix[0].y,modelMatrix[0].z));
		scale.y = length(vec3(modelMatrix[1].x,modelMatrix[1].y,modelMatrix[1].z));
		vec2 alignedPosition = (position.xy - (center - vec2(0.5))) * scale;
		vec2 rotatedPosition;
		rotatedPosition.x = cos(rotation) * alignedPosition.x - sin(rotation) * alignedPosition.y;
		rotatedPosition.y = sin(rotation) * alignedPosition.x - cos(rotation) * alignedPosition.y;
		mvPosition.xy += rotatedPosition;

		gl_Position = projectionMatrix * mvPosition;

		#include <logdepthbuf_vertex>
		#include <clipping_planes_vertex>
		#include <fog_vertex>
	}
`;

● 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 textureArray;
let plane;
let time = 0;

function init(){
	setLoading();
}

function setLoading(){
	TweenMax.to('.loader',0.1,{opacity:1});

	const nameArray = ['texture','shape'];
	let manifestArray =[];

	for(let i = 0; i < nameArray.length; i++){
		let name = nameArray[i];
		let path = 'img/' + name + '.jpg';
		manifestArray.push({id:name,src:path});
	}

	const loadQueue = new createjs.LoadQueue();
		loadQueue.on('progress',function(e){
		const progress = e.progress;
	});

	textureArray = [];
	loadQueue.on('complete',function(){
		for(let i = 0; i < nameArray.length; i++){
			let tempImage = loadQueue.getResult(nameArray[i]);
			let tempTexture = new THREE.Texture(tempImage);
			tempTexture.needsUpdate = true;
			textureArray.push(tempTexture);
		}
		TweenMax.to('#loader_wrapper',1,{
			opacity:0,
			onComplete: function(){
				document.getElementById('loader_wrapper').style.display = 'none';
				TweenMax.to('.loader',0,{opacity:0});
			}
		});
		threeWorld();
		setLight();
		setControll();
		rendering();
	});

	loadQueue.loadManifest(manifestArray);
}

//===============================================================
// Create World
//===============================================================
function threeWorld(){
	renderer.outputEncoding = THREE.sRGBEncoding;

	const gridHelper = new THREE.GridHelper(50,50);
	gridHelper.position.y = -2.5;
	scene.add(gridHelper);

	const uniforms = {
		time:{type:'f',value:0.0},
 	  	uFac1: {value: 17.8},
		uFac2: {value: 2.7},
		uTexture1:{type:'t',value:null},
		uTexture2:{type:'t',value:null},
		uTimeFactor1:{value:0.002},
		uTimeFactor2:{value:0.0015},
		uDisplStrength1:{value:0.04},
		uDisplStrenght2:{value:0.08},
	};
	const geometry = new THREE.PlaneGeometry(7,7,2,2);
	const material = new THREE.RawShaderMaterial({
		vertexShader:vertexShader,
		fragmentShader:fragmentShader,
		uniforms: {
			...THREE.UniformsUtils.clone(THREE.ShaderLib.sprite.uniforms),
			...uniforms
		},
		side:THREE.BackSide,
		transparent:true
	});
	uniforms.uTexture1.value = textureArray[0];
	uniforms.uTexture2.value = textureArray[1];
	uniforms.uTexture1.value.wrapS = uniforms.uTexture1.value.wrapT = THREE.RepeatWrapping;
	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);
}

● glsl.js

const vertexShader =`
	precision highp float;

	attribute vec3 position;
    attribute vec2 uv;

    uniform float rotation;
    uniform vec2 center;
    uniform mat4 modelMatrix;
    uniform mat4 modelViewMatrix;
	uniform mat4 projectionMatrix;

	varying vec2 vUv;

	#include <common>
	#include <uv_pars_vertex>
	#include <fog_pars_vertex>
	#include <logdepthbuf_pars_vertex>
	#include <clipping_planes_pars_vertex>

	void main(void){
		vUv = uv;

		vec4 mvPosition = modelViewMatrix * vec4(0.0,0.0,0.0,1.0);
		vec2 scale;

		scale.x = length(vec3(modelMatrix[0].x,modelMatrix[0].y,modelMatrix[0].z));
		scale.y = length(vec3(modelMatrix[1].x,modelMatrix[1].y,modelMatrix[1].z));
		vec2 alignedPosition = (position.xy - (center - vec2(0.5))) * scale;
		vec2 rotatedPosition;
		rotatedPosition.x = cos(rotation) * alignedPosition.x - sin(rotation) * alignedPosition.y;
		rotatedPosition.y = sin(rotation) * alignedPosition.x - cos(rotation) * alignedPosition.y;
		mvPosition.xy += rotatedPosition;

		gl_Position = projectionMatrix * mvPosition;

		#include <logdepthbuf_vertex>
		#include <clipping_planes_vertex>
		#include <fog_vertex>
	}
`;

const fragmentShader =`
	precision highp float;

	uniform sampler2D uTexture1;
	uniform sampler2D uTexture2;
	uniform float time;
	uniform float uFac1;
	uniform float uFac2;
	uniform float uTimeFactor1;
	uniform float uTimeFactor2;
	uniform float uDisplStrength1;
	uniform float uDisplStrength2;

	varying vec2 vUv;

    // Description : Array and textureless GLSL 2D/3D/4D simplex noise functions.
    //      Author : Ian McEwan,Ashima Arts.
    //  Maintainer : stegu
    //     Lastmod : 20201014(stegu)
    //     License : Copyright(C)2011 Ashima Arts.All rights reserved.
    //               Distributed under the MIT License.See LICENSE file.
    //               https://github.com/ashima/webgl-noise
    //               https://github.com/stegu/webgl-noise

    vec3 mod289(vec3 x){ return x - floor(x * (1.0 / 289.0)) * 289.0; }
    vec4 mod289(vec4 x){ return x - floor(x * (1.0 / 289.0)) * 289.0; }
    vec4 permute(vec4 x){ return mod289(((x*34.0)+1.0)*x); }
    vec4 taylorInvSqrt(vec4 r){ return 1.79284291400159 - 0.85373472095314 * r; }

    float snoise(vec3 v){
        const vec2 C = vec2(1.0/6.0, 1.0/3.0);
        const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
        vec3 i  = floor(v + dot(v, C.yyy) );
        vec3 x0 = v - i + dot(i, C.xxx) ;
        vec3 g = step(x0.yzx, x0.xyz);
        vec3 l = 1.0 - g;
        vec3 i1 = min( g.xyz, l.zxy );
        vec3 i2 = max( g.xyz, l.zxy );
        vec3 x1 = x0 - i1 + C.xxx;
        vec3 x2 = x0 - i2 + C.yyy;
        vec3 x3 = x0 - D.yyy;
        i = mod289(i);
        vec4 p = permute(permute(permute(
                 i.z + vec4(0.0, i1.z, i2.z, 1.0))
               + i.y + vec4(0.0, i1.y, i2.y, 1.0))
               + i.x + vec4(0.0, i1.x, i2.x, 1.0));
        float n_ = 0.142857142857;
        vec3 ns = n_ * D.wyz - D.xzx;
        vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
        vec4 x_ = floor(j * ns.z);
        vec4 y_ = floor(j - 7.0 * x_);
        vec4 x = x_ * ns.x + ns.yyyy;
        vec4 y = y_ * ns.x + ns.yyyy;
        vec4 h = 1.0 - abs(x) - abs(y);
        vec4 b0 = vec4(x.xy, y.xy);
        vec4 b1 = vec4(x.zw, y.zw);
        vec4 s0 = floor(b0)*2.0 + 1.0;
        vec4 s1 = floor(b1)*2.0 + 1.0;
        vec4 sh = -step(h, vec4(0.0));
        vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
        vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
        vec3 p0 = vec3(a0.xy,h.x);
        vec3 p1 = vec3(a0.zw,h.y);
        vec3 p2 = vec3(a1.xy,h.z);
        vec3 p3 = vec3(a1.zw,h.w);
        vec4 norm = taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2, p2),dot(p3,p3)));
        p0 *= norm.x;
        p1 *= norm.y;
        p2 *= norm.z;
        p3 *= norm.w;
        vec4 m = max(0.5 - vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0);
        m = m * m;
        return 105.0 * dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3)));
    }

    float fbm3d(vec3 x, const in int it) {
	    float v = 0.0;
	    float a = 0.5;
	    vec3 shift = vec3(100);

	    for (int i = 0; i < 32; ++i) {
	        if(i < it) {
	            v += a * snoise(x);
	            x = x * 2.0 + shift;
	            a *= 0.5;
	        }
	    }
	    return v;
	}

	vec4 gammaCorrect(vec4 color, float gamma){
		return pow(color, vec4(1.0 / gamma));
	}
	vec4 levelRange(vec4 color, float minInput, float maxInput){
		return min(max(color - vec4(minInput), vec4(0.0)) / (vec4(maxInput) - vec4(minInput)), vec4(1.0));
	}
	vec4 levels(vec4 color, float minInput, float gamma, float maxInput){
		return gammaCorrect(levelRange(color, minInput, maxInput), gamma);
	}

    void main(void){
    	vec2 newUv = vUv;

    	vec4 txtNoise1 = texture2D(uTexture1,vec2(vUv.x + time * 0.0001,vUv.y - time * 0.00014));
    	vec4 txtNoise2 = texture2D(uTexture1,vec2(vUv.x - time * 0.00002,vUv.y + time * 0.000017 + 0.2));

    	float noiseBig = fbm3d(vec3(vUv * uFac1,time * uTimeFactor1),4) + 1.0 * 0.5;
    	newUv += noiseBig * uDisplStrength1;

    	float noiseSmall = snoise(vec3(newUv * uFac2,time * uTimeFactor2)) + 1.0 * 0.5;
    	newUv += noiseSmall * uDisplStrength2;

    	vec4 txtShape = texture2D(uTexture2,newUv);
    	float alpha = levels((txtNoise1 + txtNoise2) * 0.6,0.2,0.4,0.7).r;
    	alpha *= txtShape.r;

    	gl_FragColor = vec4(vec3(0.95,0.95,0.95),alpha);
	}
`;

export { vertexShader, fragmentShader };

● 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,0,6);
	scene.add(camera);

	renderer = new THREE.WebGLRenderer({antialias:true});
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(window.innerWidth,window.innerHeight);
	renderer.setClearColor(0x036da9);

	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 }

完成したデモになります。背景を空色にして、シェーダで雲を制作しました。

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

関連記事

前の記事へ

EffectComposerで揺らぎエフェクト(2)

次の記事へ

Blender2.9でクッションを制作