Intro to WebGL with Three.js
WebGL
JavaScript API for rendering interactive 2D and 3D graphics inside an HTML < canvas > element
지원되는 브라우저 크롬, 파이어폭스, IE11+, 오페라, 사파리, iOS8+, 안드로이드크롬
three.js
threejs.org 3D Javascript Library Renderers: WebGL, < canvas >, < svg >, CSS3D/DOM, and more Scenes, Cameras, Geometry, 3D Model Loaders, Lights, Materials, Shaders, Particles, Animation, Math Utilities
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
html,body{margin:0;padding:0;overflow:hidden;}
</style>
</head>
<body>
<script src="https://threejs.org/build/three.min.js"></script>
<script>
var scene = new THREE.Scene();
var aspect = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.BoxGeometry(1,1,1);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
var render = function(){
requestAnimationFrame(render);
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
}
render();
</script>
</body>
</html>
Scene Graph
Object 3D
var group = new THREE.Group();
scene.add(group);
group.add(mesh1);
group.add(mesh2);
mesh2.visible = false;
group.remove(mesh2);
group.children;//mesh1
group.parent;//scene
Object3D Transforms
mesh.position.x = 0;
mesh.position.x = -100;
mesh.scale.set(2,2,2);
mesh.rotation.y = Math.PI / 4;
mesh.rotation.y = Math.PI * 5 / 4
Unit Circle
mesh.rotation.y = THREE.Math.degToRad(45);
mesh.position.x = Math.cos(time);
mesh.position.y = Math.sin(time);
Camera
cam = new THREE.PerspectiveCamera(fov, aspect, near, far);
cam.fov = 15;
cam.fov = 60;
// 참고 : http://davidscottlyons.com/threejs-intro/#slide-33
cam.far = 1000;
cam.far = 3000;
// 참고 : http://davidscottlyons.com/threejs-intro/#slide-35
cam = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
Camera Controls
OrbitControls
Orbit controls을 사용하면 카메라가 대상 주위를 공전 할 수 있습니다.
<script src="path/to/OrbitControls.js"></script>
controls = new THREE.OrbitControls( camera );
function render() {
requestAnimationFrame( render );
controls.update();
renderer.render( scene, camera );
}
controls.enablePan = false;
controls.enableZoom = false;
controls.enableRotate = false;
controls.minDistance
controls.maxDistance
controls.minPolarAngle
controls.maxPolarAngle
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
html,body{margin:0;padding:0;overflow:hidden;}
</style>
</head>
<body>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/141228/OrbitControls.js"></script>
<script>
var scene = new THREE.Scene();
var aspect = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//Show Axis
var axes = new THREE.AxisHelper(10);
scene.add(axes);
var geometry = new THREE.BoxGeometry(1,1,1);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled,컨트롤에 무게감을주는 데 사용할 수있는 댐핑 (관성)을 활성화하려면 true로 설정하십시오. 기본값은 false입니다.
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;//패닝시 카메라의 위치가 어떻게 변환되는지 정의합니다. 참이면 카메라가 화면 공간에서 이동합니다. 그렇지 않으면 카메라가 카메라의 위쪽 방향과 직교하는 평면으로 이동합니다. 기본값은 false입니다.
controls.minDistance = 2;
controls.maxDistance = 100;
controls.maxPolarAngle = Math.PI / 2;
controls.autoRotate = true; //Set to true to automatically rotate around the target.
var render = function(){
requestAnimationFrame(render);
/* cube.rotation.x += 0.1;
cube.rotation.y += 0.1; */
controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true
renderer.render(scene, camera);
}
render();
</script>
</body>
</html>
Geometry
// cube
var geo = new THREE.BoxGeometry( width, height, depth );
// sphere
var geo = new THREE.SphereGeometry( 60, 24, 16 );
// cyliner
var geo = new THREE.CylinderGeometry( ... );
// doughnut
var geo = new THREE.TorusGeometry( ... );
Materials
// MeshBasicMaterial : A material for drawing geometries in a simple shaded (flat or wireframe) way.
var material = new THREE.MeshBasicMaterial({ ... });
// MeshLambertMaterial : A material for non-shiny surfaces, without specular highlights.
var material = new THREE.MeshLambertMaterial({ ... });
// MeshPhongMaterial : A material for shiny surfaces with specular highlights.
var material = new THREE.MeshPhongMaterial({ ... });
// MeshStandardMaterial : A standard physically based material, using Metallic-Roughness workflow.
var material = new THREE.MeshStandardMaterial({ ... });
// MeshToonMaterial : An extension of the MeshPhongMaterial with toon shading.
var material = new THREE.MeshToonMaterial({ ... });
// MeshNormalMaterial : A material that maps the normal vectors to RGB colors.
var material = new THREE.MeshNormalMaterial({ ... });
Material Properties
var material = new THREE.MeshNormalMaterial({
/*
Defines whether this material is transparent. This has an effect on rendering as transparent objects need special treatment and are rendered after non-transparent objects.
When set to true, the extent to which the material is transparent is controlled by setting its opacity property.
Default is false.
*/
transparent:false,
/* Float in the range of 0.0 - 1.0 indicating how transparent the material is. A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
If the material's transparent property is not set to true, the material will remain fully opaque and this value will only affect its color.
Default is 1.0. */
opacity:1.0,
/*Whether to have depth test enabled when rendering this material. Default is true.*/
depthTest:true,
/* Whether rendering this material has any effect on the depth buffer. Default is true. */
depthWrite:true,
/* Sets the alpha value to be used when running an alpha test. The material will not be renderered if the opacity is lower than this value. Default is 0. */
alphaTest:0,
/* Defines whether this material is visible. Default is true. */
visible:true,
/* Defines which side of faces will be rendered - front, back or both. Default is THREE.FrontSide. Other options are THREE.BackSide and THREE.DoubleSide. */
side:THREE.FrontSide,
/* Define whether the material is rendered with flat shading. Default is false. */
flatShading:false,
/* Render geometry as wireframe. Default is false (i.e. render as smooth shaded). */
wireframe:false,
/* Controls wireframe thickness. Default is 1. */
wireframeLinewidth:1,
});
Texture Mapping
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
html, body{overflow: hidden;width: 100%;height: 100%;}
* { margin: 0; padding: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="./three.min.js"></script>
<script src="./OrbitControls.js"></script>
<script>
var camera, scene, renderer, pointLight, controls;
var earthMesh;
function init() {
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 5000);
camera.position.set(5, 50, 10)
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
renderer = new THREE.WebGLRenderer({
antialias: true
});
// renderer.setClearColor(0xff0000)
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target = new THREE.Vector3(0, 50, 0);
// controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.update();
controls.maxDistance = 80;
controls.minDistance = 7;
controls.enablePan = false;
var textureLoader = new THREE.TextureLoader();
// Earth
var geometry = new THREE.SphereBufferGeometry(5, 32, 32)
var material = new THREE.MeshPhongMaterial()
material.bumpMap = textureLoader.load('./earth1.jpg')
material.bumpScale = 0.05
material.map = textureLoader.load('./earth1.jpg')
material.specularMap = textureLoader.load('./earth1.jpg')
material.specular = new THREE.Color(0xfdb813)
material.shininess = 15
earthMesh = new THREE.Mesh(geometry, material)
earthMesh.position.x = 0;
earthMesh.position.y = 50;
scene.add(earthMesh)
// Ambient Light
var ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);
// Point Light
pointLight = new THREE.PointLight(0xffffff, 0.8);
pointLight.position.set(0, 50, 0);
pointLight.castShadow = true;
pointLight.shadow.mapSize.width = 1024;
pointLight.shadow.mapSize.height = 1024;
scene.add(pointLight);
window.addEventListener('resize', () => {
renderer.setSize(window.innerWidth, window.innerHeight)
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
})
earthMesh.rotation.x = 0
animate()
}
var theta = 0;
function update() {
earthMesh.rotation.y = theta/1200
theta += 0.2;
pointLight.position.x = 40 * Math.sin(THREE.Math.degToRad(theta));
// pointLight.position.y += 1 * Math.sin(THREE.Math.degToRad(theta));
pointLight.position.z = 40 * Math.cos(THREE.Math.degToRad(theta));
}
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
update();
}
window.onload = function() {
init();
};
</script>
</body>
</html>
light
// 이 빛은 마치 멀리 떨어져있는 것처럼 작동하며 그 광선은 모두 평행입니다. 이를위한 일반적인 사용 사례는 일광을 시뮬레이션하는 것입니다. 태양은 그 위치가 무한한 것으로 간주 될 수있을만큼 멀리 떨어져 있으며, 그로부터 오는 모든 광선은 평행합니다. 이 빛은 그림자를 드리울 수 있습니다
light = new THREE.DirectionalLight( 0xdddddd, 0.8 );
// 단일 지점에서 방출되는 조명입니다. 이를위한 일반적인 사용 사례는 노출 된 전구에서 방출 된 빛을 복제하는 것입니다. 이 빛은 그림자를 드리울 수 있습니다
light = new THREE.PointLight( 0xb4e7f2, 0.8 );
// 이 빛은 하나의 점에서 한 방향으로, 원뿔을 따라 더 큰 크기로 커지는 원뿔을 따라 방출됩니다. 이 빛은 그림자를 드리울 수 있습니다
light = new THREE.SpotLight( 0xb4e7f2, 0.8 );
// 이 라이트는 전체적으로 장면의 모든 오브젝트를 동일하게 비춥니다.이 라이트는 방향이 없으므로 그림자를 투사하는 데 사용할 수 없습니다.
light = new THREE.AmbientLight( 0x444444 );
Model Loader
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
html, body {margin: 0; height: 100%;}
</style>
</head>
<body>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/loaders/GLTFLoader.js"></script>
<script>
var camera, scene, renderer, controls;
var geometry, material, mesh;
var mixer, clips;
var clock = new THREE.Clock();
var modelUrl = 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf';
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.y = 1.5;
camera.position.z = 1.25;
controls = new THREE.OrbitControls( camera );
controls.update();
scene = new THREE.Scene();
geometry = new THREE.BoxGeometry( 1, 1, 1 );
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
// scene.add( mesh );
light = new THREE.HemisphereLight( 0xbbbbff, 0x444422 );
light.position.set( 0, 1, 0 );
scene.add( light );
var light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );
var light = new THREE.PointLight( 0xffffff, 1, 100 );
light.position.set( 2, 2, 2 );
scene.add( light );
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// model
var loader = new THREE.GLTFLoader();
loader.load( modelUrl,
function ( gltf ) {
mixer = new THREE.AnimationMixer( gltf.scene );
clips = gltf.animations;
scene.add( gltf.scene );
},
undefined,
function ( e ) {
console.error( e );
});
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
var delta = clock.getDelta();
if(mixer) {
mixer.update( delta );
}
renderer.render( scene, camera );
}
</script>
</body>
</html>
Interaction
Raycaster
This class is designed to assist with raycasting.
Raycasting is used for mouse picking (working out what objects in the 3d space the mouse is over) amongst other things.
// normalized device coordinates
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster = new THREE.Raycaster();
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
INTERSECTED = intersects[ 0 ].object;
example1 지구본 클릭시 회전
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
*{margin:0;padding:0;}
body{margin:0;}
canvas{width:100%;height:100%;}
</style>
</head>
<body>
<script src="https://threejs.org/build/three.min.js"></script>
<script>
// ==========================
// 초기화 부분 시작 ( 이 부분은 문서에서 한번만 수행되면 됩니다 )
// ==========================
// 3차원 세계
var scene = new THREE.Scene();
// 카메라 ( 카메라 수직 시야 각도, 가로세로 종횡비율, 시야거리 시작지점, 시야거리 끝지점
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
// 렌더러 정의 및 크기 지정, 문서에 추가하기
var renderer = new THREE.WebGLRenderer( { antialias: true, preserveDrawingBuffer: true } );
renderer.setClearColor(0xEEEEEE);
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//Show Axis
var axes = new THREE.AxisHelper(10);
scene.add(axes);
// 빛을 생성해서
var light1 = new THREE.PointLight( 0xffffff, 1, 100 );//PointLight( color : Integer, intensity : Float, distance : Number, decay : Float )
// 위치를 적당한 지점에 놓고
light1.position.set( 5, 5, 5 );
// 장면에 추가합니다.
scene.add( light1 );
var loader = new THREE.TextureLoader();
var mesh;
loader.load(
'https://picsum.photos/id/85/300/300',
function ( texture ) {
mesh = new THREE.Mesh(
new THREE.SphereGeometry( 2, 32, 32 ),
new THREE.MeshStandardMaterial({map: texture})
);
mesh.name='Box1';
scene.add(mesh);
}
);
// 카메라의 Z좌표를 물체에서 7 정도 떨어진 지점에 위치합니다.
camera.position.z = 7;
camera.position.y = 5;
camera.rotation.x = -35 * ( Math.PI / 180 );
camera.position.x = 5;
camera.rotation.y = 35 * ( Math.PI / 180 );
// ==========================
// 초기화 부분 끝
// ==========================
var framesPerSecond=60;
var speed=0;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
// 에니메이션 효과를 자동으로 주기 위한 보조 기능입니다.
var animate = function () {
// 프레임 처리
setTimeout(function() {
requestAnimationFrame(animate);
}, 1000 / framesPerSecond);
if(speed>0){
mesh.rotation.y +=speed;
speed-=0.001;
}
// 랜더링을 수행합니다.
renderer.render( scene, camera );
};
// animate()함수를 최초에 한번은 수행해주어야 합니다.
animate();
function onDocumentMouseDown(event)
{
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
if(intersects.length>0)
{
if(intersects[0].object.name=='Box1')
{
speed+=0.05;
}
}
}
document.addEventListener('mousedown', onDocumentMouseDown, false);
</script>
</body>
</html>
p5js
p5js 란
웹페이지에 자유롭게
그림을 그리고,
그림을 움직이고,
사용자와 상호작용하도록
돕는 javascript canvas 기반
무료 오픈소스 라이브러리입니다.
출처
Intro to WebGL
Watch Presentation Video
Slides on GitHub
Discover Three.js
생활코딩 p5.js