2021年05月04日 - WebVR・Three.js
Three.jsでオフスクリーンレンダリング
オフスクリーンレンダリングが使えるようになると、表現の幅が広がります。そこで「シェーダで画像エフェクト」に続き、Three.jsでオフスクリーンレンダリングを試しました。※Three.jsはr128を使用しています。
Three.jsでオフスクリーンレンダリング
オフスクリーンレンダリングは、スクリーンではなくメモリ上にレンダリングした結果を、そのままテクスチャとして使用する手法です。
● トーラスを生成
まずは、TorusGeometryを使用してトーラスを生成します。マテリアルはライティングを必要としないMeshNormalMaterialを使用します。
const geometry = new THREE.TorusGeometry(2.5,1,32,100); const material = new THREE.MeshNormalMaterial(); const torus = new THREE.Mesh(geometry,material); scene.add(torus);
● オフスクリーンレンダリング
トーラスを生成して、オフスクリーンレンダリング用のシーンに追加します。次にスクリーン用の平面にレンダーターゲットオブジェクトのテクスチャを設定して、レンダリングします。
//オフスクリーンレンダリング用のシーン const sceneRTT = new THREE.Scene(); //オフスクリーンレンダリング用のカメラ const cameraRTT = new THREE.PerspectiveCamera(50,1,1,50); cameraRTT.position.set(0,0,11); //レンダーターゲットオブジェクト const renderTarget = new THREE.WebGLRenderTarget(1024,1024); //トーラス const geometry = new THREE.TorusGeometry(2.5,1,32,100); const material = new THREE.MeshNormalMaterial(); const torus = new THREE.Mesh(geometry,material); sceneRTT.add(torus); //スクリーン用の平面 const planeGeometry = new THREE.PlaneGeometry(4,4,2,2); const planeMaterial = new THREE.MeshPhongMaterial({ color:0xFFFFFF, //テクスチャを設定 map:renderTarget.texture, }); const plane = new THREE.Mesh(planeGeometry,planeMaterial); scene.add(plane); function rendering(){ requestAnimationFrame(rendering); //オフスクリーンレンダリング renderer.setClearColor(0x115558,1.0); renderer.setRenderTarget(renderTarget); renderer.render(sceneRTT,cameraRTT); renderer.setRenderTarget(null); //レンダリング renderer.setClearColor(0x000000,1.0); 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'; //=============================================================== // Init //=============================================================== window.addEventListener('load',function(){ init(); }); let orbitControls; let torus; let sceneRTT,cameraRTT,renderTarget; 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; sceneRTT = new THREE.Scene(); cameraRTT = new THREE.PerspectiveCamera(50,1,1,50); cameraRTT.position.set(0,0,11); renderTarget = new THREE.WebGLRenderTarget(1024,1024); const geometry = new THREE.TorusGeometry(2.5,1,32,100); const material = new THREE.MeshNormalMaterial(); torus = new THREE.Mesh(geometry,material); sceneRTT.add(torus); const planeGeometry = new THREE.PlaneGeometry(4,4,2,2); const planeMaterial = new THREE.MeshPhongMaterial({ color:0xFFFFFF, shininess:70, reflectivity:1, map:renderTarget.texture, side:THREE.DoubleSide }); const plane = new THREE.Mesh(planeGeometry,planeMaterial); scene.add(plane); } function setLight(){ const ambientlight = new THREE.AmbientLight(0x333333,0.1); scene.add(ambientlight); const pointLight = new THREE.PointLight(0XFFFFFF,4,3.7,1); pointLight.position.set(0,0,3); scene.add(pointLight); const pointLightHelper = new THREE.PointLightHelper(pointLight,0.3); scene.add(pointLightHelper); } 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++; torus.rotation.x = time * 0.25 * Math.PI / 180; torus.rotation.y = time * 0.5 * Math.PI / 180; renderer.setClearColor(0x115558,1.0); renderer.setRenderTarget(renderTarget); renderer.render(sceneRTT,cameraRTT); renderer.setRenderTarget(null); renderer.setClearColor(0x000000,1.0); 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,100); camera.position.set(0,0,10); 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 }
完成したデモになります。Three.jsでオフスクリーンレンダリングを試しました。