"use strict"; // 參考 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode //////////////////////////////////////////////////////////////////////////////// // 後照攝影機作業 //////////////////////////////////////////////////////////////////////////////// var camera, rearCam, scene, renderer; var cameraControls; var clock = new THREE.Clock(); var canvasWidth; var canvasHeight; var rearTarget; function fillScene() { scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0xDDDDDD, 3000, 4000 ); // 光源 scene.add( new THREE.AmbientLight( 0x222222 ) ); var light = new THREE.DirectionalLight( 0xFFFFFF, 0.7 ); light.position.set( 200, 500, 500 ); scene.add( light ); light = new THREE.DirectionalLight( 0xFFFFFF, 0.9 ); light.position.set( -200, -100, -400 ); scene.add( light ); // 一些物件 var x,z; for ( x = -4500; x <= 4500; x += 1000 ) { for ( z = -4500; z <= 4500; z += 1000 ) { var coneMaterial = new THREE.MeshLambertMaterial(); // 色輪 var coneDistance = Math.sqrt( x*x + z*z ); // 將值從角度轉換到 2*PI 範圍 var colorHue = Math.acos( x / coneDistance ); if ( z > 0 ) { colorHue = Math.PI*2 - colorHue; } var colorSat = 1 - (coneDistance / 6364); coneMaterial.color.setHSL((colorHue+1)/(Math.PI*2), colorSat, 0.6 ); coneMaterial.ambient.copy( coneMaterial.color ); var cone = new THREE.Mesh( new THREE.CylinderGeometry( 100 - colorSat*100, colorSat*100, 300 ), coneMaterial ); cone.position.set( x, 150, z ); scene.add( cone ); } } // 材質 var headMaterial = new THREE.MeshLambertMaterial( ); headMaterial.color.r = 104/255; headMaterial.color.g = 1/255; headMaterial.color.b = 5/255; var hatMaterial = new THREE.MeshPhongMaterial( { shininess: 100 } ); hatMaterial.color.r = 24/255; hatMaterial.color.g = 38/255; hatMaterial.color.b = 77/255; hatMaterial.specular.setRGB( 0.5, 0.5, 0.5 ); var bodyMaterial = new THREE.MeshPhongMaterial( { shininess: 100 } ); bodyMaterial.color.setRGB( 31/255, 86/255, 169/255 ); bodyMaterial.specular.setRGB( 0.5, 0.5, 0.5 ); var glassMaterial = new THREE.MeshPhongMaterial( { color: 0x0, specular: 0xFFFFFF, shininess: 100, opacity: 0.3, transparent: true } ); var legMaterial = new THREE.MeshPhongMaterial( { shininess: 4 } ); legMaterial.color.setHex( 0xAdA79b ); legMaterial.specular.setRGB( 0.5, 0.5, 0.5 ); var footMaterial = new THREE.MeshPhongMaterial( { color: 0x960f0b, shininess: 30 } ); footMaterial.specular.setRGB( 0.5, 0.5, 0.5 ); var sphere, cylinder, cube; var bevelRadius = 1.9; // TODO: 2.0 會造成幾何問題 // 建模 // 基底 cube = new THREE.Mesh( new THREE.BeveledBlockGeometry( 20+64+110, 4, 2*77+12, bevelRadius ), footMaterial ); cube.position.x = -45; // (20+32) - 寬度的一半 (20+64+110)/2 cube.position.y = 4/2; // 高的的一半 cube.position.z = 0; // 中心在原點 scene.add( cube ); // 腳掌 cube = new THREE.Mesh( new THREE.BeveledBlockGeometry( 20+64+110, 52, 6, bevelRadius ), footMaterial ); cube.position.x = -45; // (20+32) - 寬度的一半 (20+64+110)/2 cube.position.y = 52/2; // 高的的一半 cube.position.z = 77 + 6/2; // 偏移 77 + 深度的一半 6/2 scene.add( cube ); cube = new THREE.Mesh( new THREE.BeveledBlockGeometry( 20+64+110, 52, 6, bevelRadius ), footMaterial ); cube.position.x = -45; // (20+32) - 寬度的一半 (20+64+110)/2 cube.position.y = 52/2; // 高的的一半 cube.position.z = -(77 + 6/2); // 負的偏移 77 + 深度的一半 6/2 scene.add( cube ); cube = new THREE.Mesh( new THREE.BeveledBlockGeometry( 64, 104, 6, bevelRadius ), footMaterial ); cube.position.x = 0; // X 方向在原點 cube.position.y = 104/2; cube.position.z = 77 + 6/2; // 負的偏移 77 + 深度的一半 6/2 scene.add( cube ); cube = new THREE.Mesh( new THREE.BeveledBlockGeometry( 64, 104, 6, bevelRadius ), footMaterial ); cube.position.x = 0; // X 方向在原點 cube.position.y = 104/2; cube.position.z = -(77 + 6/2); // 負的偏移 77 + 深度的一半 6/2 scene.add( cube ); // 腳 cube = new THREE.Mesh( new THREE.BeveledBlockGeometry( 60, 282+4, 4, bevelRadius ), legMaterial ); cube.position.x = 0; // X 方向在原點 cube.position.y = 104 + 282/2 - 2; cube.position.z = 77 + 6/2; // 負的偏移 77 + 深度的一半 6/2 scene.add( cube ); cube = new THREE.Mesh( new THREE.BeveledBlockGeometry( 60, 282+4, 4, bevelRadius ), legMaterial ); cube.position.x = 0; // X 方向在原點 cube.position.y = 104 + 282/2 - 2; cube.position.z = -(77 + 6/2); // 負的偏移 77 + 深度的一半 6/2 scene.add( cube ); // 身體 sphere = new THREE.Mesh( new THREE.SphereGeometry( 104/2, 32, 16, 0, Math.PI * 2, Math.PI/2, Math.PI ), bodyMaterial ); sphere.position.x = 0; sphere.position.y = 160; sphere.position.z = 0; scene.add( sphere ); // 半球的上蓋 cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 104/2, 104/2, 0, 32 ), bodyMaterial ); cylinder.position.x = 0; cylinder.position.y = 160; cylinder.position.z = 0; scene.add( cylinder ); cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 12/2, 12/2, 390 - 100, 32 ), bodyMaterial ); cylinder.position.x = 0; cylinder.position.y = 160 + 390/2 - 100; cylinder.position.z = 0; scene.add( cylinder ); // 玻璃 sphere = new THREE.Mesh( new THREE.SphereGeometry( 116/2, 32, 16 ), glassMaterial ); sphere.position.x = 0; sphere.position.y = 160; sphere.position.z = 0; scene.add( sphere ); cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 24/2, 24/2, 390, 32 ), glassMaterial ); cylinder.position.x = 0; cylinder.position.y = 160 + 390/2; cylinder.position.z = 0; scene.add( cylinder ); // 頭 sphere = new THREE.Mesh( new THREE.SphereGeometry( 104/2, 32, 16 ), headMaterial ); sphere.position.x = 0; sphere.position.y = 160 + 390; sphere.position.z = 0; scene.add( sphere ); // 帽子 cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 142/2, 142/2, 10, 32 ), hatMaterial ); cylinder.position.x = 0; cylinder.position.y = 160 + 390 + 40 + 10/2; cylinder.position.z = 0; scene.add( cylinder ); cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 80/2, 80/2, 70, 32 ), hatMaterial ); cylinder.position.x = 0; cylinder.position.y = 160 + 390 + 40 + 10 + 70/2; cylinder.position.z = 0; scene.add( cylinder ); var crossbarMaterial = new THREE.MeshPhongMaterial( { color: 0x808080, specular: 0xFFFFFF, shininess: 400 } ); var eyeMaterial = new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x303030, shininess: 4 } ); // 橫桿 cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 5, 5, 200, 32 ), crossbarMaterial ); cylinder.position.set( 0, 360, 0 ); cylinder.rotation.x = 90 * Math.PI / 180.0; scene.add( cylinder ); // 鼻子 cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 6, 14, 70, 32 ), headMaterial ); cylinder.position.set( -70, 530, 0 ); cylinder.rotation.z = 90 * Math.PI / 180.0; scene.add( cylinder ); // 眼睛 var sphGeom = new THREE.SphereGeometry( 10, 32, 16 ); // 左眼 sphere = new THREE.Mesh( sphGeom, eyeMaterial ); sphere.position.set( -48, 560, 0 ); var eye = new THREE.Object3D(); eye.add( sphere ); eye.rotation.y = 20 * Math.PI / 180.0; scene.add( eye ); // 右眼 sphere = new THREE.Mesh( sphGeom, eyeMaterial ); sphere.position.set( -48, 560, 0 ); eye = new THREE.Object3D(); eye.add( sphere ); eye.rotation.y = -20 * Math.PI / 180.0; scene.add( eye ); } function init() { canvasWidth = window.innerWidth; canvasHeight = window.innerHeight; // 配置一次,而非每影格配置 rearTarget = new THREE.Vector3(); // RENDERER renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.setSize(canvasWidth, canvasHeight); renderer.setClearColorHex( 0xDDDDDD, 1.0 ); // 第二視埠繪製時不要清除螢幕 renderer.autoClear = false; // 攝影機 camera = new THREE.PerspectiveCamera( 45, canvasWidth / canvasHeight, 1, 7000 ); camera.position.set( -810, 310, -1230 ); rearCam = new THREE.PerspectiveCamera( 45, canvasWidth / canvasHeight, 1, 7000 ); rearCam.position.set( -810, 310, -1230 ); rearCam.lookAt( new THREE.Vector3( 0, 0, 0 ) ); // 控制 cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement); cameraControls.target.set(0,130,0); } function drawHelpers() { Coordinates.drawGround({size:10000}); Coordinates.drawGrid({size:10000,scale:0.01}); } function addToDOM() { var container = document.getElementById('container'); var canvas = container.getElementsByTagName('canvas'); if (canvas.length>0) { container.removeChild(canvas[0]); } container.appendChild( renderer.domElement ); } function animate() { window.requestAnimationFrame(animate); render(); } function render() { var delta = clock.getDelta(); cameraControls.update(delta); renderer.enableScissorTest( false ); renderer.setViewport( 0, 0, canvasWidth, canvasHeight ); renderer.clear(); renderer.render( scene, camera ); // 學生作業:設定 rearCam 使它看向相反方向 // 後照 render renderer.enableScissorTest( true ); // setScissor 在這裡可以改成只設定一次 // 因為範圍不會改變 renderer.setScissor( 0.75 * canvasWidth, 0.75 * canvasHeight, 0.25 * canvasWidth, 0.25 * canvasHeight ); renderer.setViewport( 0.75 * canvasWidth, 0.75 * canvasHeight, 0.25 * canvasWidth, 0.25 * canvasHeight ); renderer.clear(); renderer.render( scene, rearCam ); } document.addEventListener("DOMContentLoaded", function () { try { init(); fillScene(); drawHelpers(); addToDOM(); animate(); } catch(e) { var errorReport = "你的程式遇到不可復原的錯誤,無法繪製 Canvas。錯誤是:<br/><br/>"; $('#container').append(errorReport+e); } })