第四课讲的是变换,还有坐标系之类的,不是很深。
第四课的Problem set记录。
1.机器人手臂
要求是添加机器人手臂的底座,只要理解好THREE.Object3D()就可以了。参考一下原有的代码就可以得到结果。
//////////////////////////////////////////////////////////////////////////////// /*global THREE, Coordinates, $, document, window, dat*/ var camera, scene, renderer; var cameraControls, effectController; var clock = new THREE.Clock(); var gridX = true; var gridY = false; var gridZ = false; var axes = true; var ground = true; var arm, forearm, body; function fillScene() { scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0x808080, 2000, 4000 ); // LIGHTS var ambientLight = new THREE.AmbientLight( 0x222222 ); var light = new THREE.DirectionalLight( 0xffffff, 1.0 ); light.position.set( 200, 400, 500 ); var light2 = new THREE.DirectionalLight( 0xffffff, 1.0 ); light2.position.set( -500, 250, -200 ); scene.add(ambientLight); scene.add(light); scene.add(light2); // Robot definitions var robotBaseMaterial = new THREE.MeshPhongMaterial( { color: 0x6E23BB, specular: 0x6E23BB, shininess: 20 } ); var robotForearmMaterial = new THREE.MeshPhongMaterial( { color: 0xF4C154, specular: 0xF4C154, shininess: 100 } ); var robotUpperArmMaterial = new THREE.MeshPhongMaterial( { color: 0x95E4FB, specular: 0x95E4FB, shininess: 100 } ); var robotBodyMaterial = new THREE.MeshPhongMaterial( { color: 0x279933, specular: 0x279933, shininess: 100 } ); var torus = new THREE.Mesh( new THREE.TorusGeometry( 22, 15, 32, 32 ), robotBaseMaterial ); torus.rotation.x = 90 * Math.PI/180; scene.add( torus ); forearm = new THREE.Object3D(); var faLength = 80; createRobotExtender( forearm, faLength, robotForearmMaterial ); arm = new THREE.Object3D(); var uaLength = 120; createRobotCrane( arm, uaLength, robotUpperArmMaterial ); // Move the forearm itself to the end of the upper arm. forearm.position.y = uaLength; arm.add( forearm ); //scene.add( arm ); // YOUR CODE HERE body = new THREE.Object3D(); var bodyLength = 60; arm.position.y = bodyLength; body.add( arm ); createRobotBody( body, bodyLength, robotBodyMaterial ); scene.add( body ); } function createRobotExtender( part, length, material ) { var cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 22, 22, 6, 32 ), material ); part.add( cylinder ); var i; for ( i = 0; i < 4; i++ ) { var box = new THREE.Mesh( new THREE.CubeGeometry( 4, length, 4 ), material ); box.position.x = (i < 2) ? -8 : 8; box.position.y = length/2; box.position.z = (i%2) ? -8 : 8; part.add( box ); } cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 15, 15, 40, 32 ), material ); cylinder.rotation.x = 90 * Math.PI/180; cylinder.position.y = length; part.add( cylinder ); } function createRobotCrane( part, length, material ) { var box = new THREE.Mesh( new THREE.CubeGeometry( 18, length, 18 ), material ); box.position.y = length/2; part.add( box ); var sphere = new THREE.Mesh( new THREE.SphereGeometry( 20, 32, 16 ), material ); // place sphere at end of arm sphere.position.y = length; part.add( sphere ); } function createRobotBody( part, length, material ) { var cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 50, 12, length/2, 18 ), material ); cylinder.position.y = length/4; part.add( cylinder ); cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 12, 50, length/2, 18 ), material ); cylinder.position.y = 3*length/4; part.add( cylinder ); var box = new THREE.Mesh( new THREE.CubeGeometry( 12, length/4, 110 ), material ); box.position.y = length/2; part.add( box ); var sphere = new THREE.Mesh( new THREE.SphereGeometry( 20, 32, 16 ), material ); // place sphere at end of arm sphere.position.y = length; part.add( sphere ); } function init() { var canvasWidth = 846; var canvasHeight = 494; var canvasRatio = canvasWidth / canvasHeight; // RENDERER renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.setSize(canvasWidth, canvasHeight); renderer.setClearColorHex( 0xAAAAAA, 1.0 ); // CAMERA camera = new THREE.PerspectiveCamera( 38, canvasRatio, 1, 10000 ); camera.position.set( -510, 240, 100 ); // CONTROLS cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement); cameraControls.target.set(0,120,0); camera.position.set(-102, 177, 20); cameraControls.target.set(-13, 60, 2); fillScene(); } 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 drawHelpers() { if (ground) { Coordinates.drawGround({size:10000}); } if (gridX) { Coordinates.drawGrid({size:10000,scale:0.01}); } if (gridY) { Coordinates.drawGrid({size:10000,scale:0.01, orientation:"y"}); } if (gridZ) { Coordinates.drawGrid({size:10000,scale:0.01, orientation:"z"}); } if (axes) { Coordinates.drawAllAxes({axisLength:200,axisRadius:1,axisTess:50}); } } function animate() { window.requestAnimationFrame(animate); render(); } function render() { var delta = clock.getDelta(); cameraControls.update(delta); if ( effectController.newGridX !== gridX || effectController.newGridY !== gridY || effectController.newGridZ !== gridZ || effectController.newGround !== ground || effectController.newAxes !== axes) { gridX = effectController.newGridX; gridY = effectController.newGridY; gridZ = effectController.newGridZ; ground = effectController.newGround; axes = effectController.newAxes; fillScene(); } // UNCOMMENT FOLLOWING LINES TO ENABLE CONTROLS FOR BODY: // body.rotation.y = effectController.by * Math.PI/180; // yaw arm.rotation.y = effectController.uy * Math.PI/180; // yaw arm.rotation.z = effectController.uz * Math.PI/180; // roll forearm.rotation.y = effectController.fy * Math.PI/180; // yaw forearm.rotation.z = effectController.fz * Math.PI/180; // roll renderer.render(scene, camera); } function setupGui() { effectController = { newGridX: gridX, newGridY: gridY, newGridZ: gridZ, newGround: ground, newAxes: axes, // UNCOMMENT FOLLOWING LINE TO SET DEFAULT VALUE OF CONTROLS FOR BODY: // by: 0.0, uy: 70.0, uz: -15.0, fy: 10.0, fz: 60.0 }; var gui = new dat.GUI(); var h = gui.addFolder("Grid display"); h.add( effectController, "newGridX").name("Show XZ grid"); h.add( effectController, "newGridY" ).name("Show YZ grid"); h.add( effectController, "newGridZ" ).name("Show XY grid"); h.add( effectController, "newGround" ).name("Show ground"); h.add( effectController, "newAxes" ).name("Show axes"); h = gui.addFolder("Arm angles"); // student, uncomment: h.add(effectController, "by", -180.0, 180.0, 0.025).name("Body y"); h.add(effectController, "uy", -180.0, 180.0, 0.025).name("Upper arm y"); h.add(effectController, "uz", -45.0, 45.0, 0.025).name("Upper arm z"); h.add(effectController, "fy", -180.0, 180.0, 0.025).name("Forearm y"); h.add(effectController, "fz", -120.0, 120.0, 0.025).name("Forearm z"); } init(); fillScene(); drawHelpers(); addToDOM(); setupGui(); animate();
2.添加机器人手指
首先参照leftHand的创建方式创建rightHand,然后在render()方法中添加对手指的控制。
//////////////////////////////////////////////////////////////////////////////// /*global THREE, Coordinates, $, document, window, dat*/ var camera, scene, renderer; var cameraControls, effectController; var clock = new THREE.Clock(); var gridX = true; var gridY = false; var gridZ = false; var axes = true; var ground = true; var arm, forearm, body, handLeft, handRight; function fillScene() { scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0x808080, 2000, 4000 ); // LIGHTS var ambientLight = new THREE.AmbientLight( 0x222222 ); var light = new THREE.DirectionalLight( 0xffffff, 1.0 ); light.position.set( 200, 400, 500 ); var light2 = new THREE.DirectionalLight( 0xffffff, 1.0 ); light2.position.set( -500, 250, -200 ); scene.add(ambientLight); scene.add(light); scene.add(light2); // Robot definitions var robotHandLeftMaterial = new THREE.MeshPhongMaterial( { color: 0xCC3399, specular: 0xCC3399, shininess: 20 } ); var robotHandRightMaterial = new THREE.MeshPhongMaterial( { color: 0xDD3388, specular: 0xDD3388, shininess: 20 } ); var robotBaseMaterial = new THREE.MeshPhongMaterial( { color: 0x6E23BB, specular: 0x6E23BB, shininess: 20 } ); var robotForearmMaterial = new THREE.MeshPhongMaterial( { color: 0xF4C154, specular: 0xF4C154, shininess: 100 } ); var robotUpperArmMaterial = new THREE.MeshPhongMaterial( { color: 0x95E4FB, specular: 0x95E4FB, shininess: 100 } ); var torus = new THREE.Mesh( new THREE.TorusGeometry( 22, 15, 32, 32 ), robotBaseMaterial ); torus.rotation.x = 90 * Math.PI/180; scene.add( torus ); forearm = new THREE.Object3D(); var faLength = 80; createRobotExtender( forearm, faLength, robotForearmMaterial ); arm = new THREE.Object3D(); var uaLength = 120; createRobotCrane( arm, uaLength, robotUpperArmMaterial ); // Move the forearm itself to the end of the upper arm. forearm.position.y = uaLength; arm.add( forearm ); scene.add( arm ); var handLength = 38; handLeft = new THREE.Object3D(); createRobotGrabber( handLeft, handLength, robotHandLeftMaterial ); // Move the hand part to the end of the forearm. handLeft.position.y = faLength; forearm.add( handLeft ); // YOUR CODE HERE // Add the second grabber handRight. Note that it uses a different color, defined above handRight= new THREE.Object3D(); createRobotGrabber( handRight, handLength, robotHandRightMaterial ); // Move the hand part to the end of the forearm. handRight.position.y = faLength; forearm.add( handRight ); // ALSO EDIT render() TO ENABLE CONTROLS FOR GRABBER } function createRobotGrabber( part, length, material ) { var box = new THREE.Mesh( new THREE.CubeGeometry( 30, length, 4 ), material ); box.position.y = length/2; part.add( box ); } function createRobotExtender( part, length, material ) { var cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 22, 22, 6, 32 ), material ); part.add( cylinder ); var i; for ( i = 0; i < 4; i++ ) { var box = new THREE.Mesh( new THREE.CubeGeometry( 4, length, 4 ), material ); box.position.x = (i < 2) ? -8 : 8; box.position.y = length/2; box.position.z = (i%2) ? -8 : 8; part.add( box ); } cylinder = new THREE.Mesh( new THREE.CylinderGeometry( 15, 15, 40, 32 ), material ); cylinder.rotation.x = 90 * Math.PI/180; cylinder.position.y = length; part.add( cylinder ); } function createRobotCrane( part, length, material ) { var box = new THREE.Mesh( new THREE.CubeGeometry( 18, length, 18 ), material ); box.position.y = length/2; part.add( box ); var sphere = new THREE.Mesh( new THREE.SphereGeometry( 20, 32, 16 ), material ); // place sphere at end of arm sphere.position.y = length; part.add( sphere ); } function init() { var canvasWidth = 846; var canvasHeight = 494; var canvasRatio = canvasWidth / canvasHeight; // RENDERER renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.setSize(canvasWidth, canvasHeight); renderer.setClearColorHex( 0xAAAAAA, 1.0 ); // CAMERA camera = new THREE.PerspectiveCamera( 38, canvasRatio, 1, 10000 ); // CONTROLS cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement); camera.position.set(-49, 242,54); cameraControls.target.set(54, 106, 33); fillScene(); } 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 drawHelpers() { if (ground) { Coordinates.drawGround({size:10000}); } if (gridX) { Coordinates.drawGrid({size:10000,scale:0.01}); } if (gridY) { Coordinates.drawGrid({size:10000,scale:0.01, orientation:"y"}); } if (gridZ) { Coordinates.drawGrid({size:10000,scale:0.01, orientation:"z"}); } if (axes) { Coordinates.drawAllAxes({axisLength:200,axisRadius:1,axisTess:50}); } } function animate() { window.requestAnimationFrame(animate); render(); } function render() { var delta = clock.getDelta(); cameraControls.update(delta); if ( effectController.newGridX !== gridX || effectController.newGridY !== gridY || effectController.newGridZ !== gridZ || effectController.newGround !== ground || effectController.newAxes !== axes) { gridX = effectController.newGridX; gridY = effectController.newGridY; gridZ = effectController.newGridZ; ground = effectController.newGround; axes = effectController.newAxes; fillScene(); } arm.rotation.y = effectController.uy * Math.PI/180; // yaw arm.rotation.z = effectController.uz * Math.PI/180; // roll forearm.rotation.y = effectController.fy * Math.PI/180; // yaw forearm.rotation.z = effectController.fz * Math.PI/180; // roll // ADD handRight yaw AND translate HERE handLeft.rotation.z = effectController.hz * Math.PI/180; // yaw handLeft.position.z = effectController.htz; // translate handRight.rotation.z = effectController.hz * Math.PI/180; handRight.position.z = -effectController.htz; renderer.render(scene, camera); } function setupGui() { effectController = { newGridX: gridX, newGridY: gridY, newGridZ: gridZ, newGround: ground, newAxes: axes, uy: 70.0, uz: -15.0, fy: 10.0, fz: 60.0, hz: 30.0, htz: 12.0 }; var gui = new dat.GUI(); var h = gui.addFolder("Grid display"); h.add( effectController, "newGridX").name("Show XZ grid"); h.add( effectController, "newGridY" ).name("Show YZ grid"); h.add( effectController, "newGridZ" ).name("Show XY grid"); h.add( effectController, "newGround" ).name("Show ground"); h.add( effectController, "newAxes" ).name("Show axes"); h = gui.addFolder("Arm angles"); h.add(effectController, "uy", -180.0, 180.0, 0.025).name("Upper arm y"); h.add(effectController, "uz", -45.0, 45.0, 0.025).name("Upper arm z"); h.add(effectController, "fy", -180.0, 180.0, 0.025).name("Forearm y"); h.add(effectController, "fz", -120.0, 120.0, 0.025).name("Forearm z"); h.add(effectController, "hz", -45.0, 45.0, 0.025).name("Hand z"); h.add(effectController, "htz", 2.0, 17.0, 0.025).name("Hand spread"); } init(); fillScene(); drawHelpers(); addToDOM(); setupGui(); animate();
3.画一朵花
这里给出了花的柄,要求画出24朵花瓣。
花瓣的绘制方法已经给出,通过循环变换就可以绘制出来,注意变换的顺序。
//////////////////////////////////////////////////////////////////////////////// // Make a Flower //////////////////////////////////////////////////////////////////////////////// /*global THREE, Coordinates, document, window, dat*/ var camera, scene, renderer; var cameraControls, effectController; var clock = new THREE.Clock(); var gridX = true; var gridY = false; var gridZ = false; var axes = true; var ground = true; function fillScene() { scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0x808080, 2000, 4000 ); // LIGHTS var ambientLight = new THREE.AmbientLight( 0x222222 ); var light = new THREE.DirectionalLight( 0xffffff, 1.0 ); light.position.set( 200, 400, 500 ); var light2 = new THREE.DirectionalLight( 0xffffff, 1.0 ); light2.position.set( -500, 250, -200 ); scene.add(ambientLight); scene.add(light); scene.add(light2); // FLOWER var petalMaterial = new THREE.MeshLambertMaterial( { color: 0xCC5920 } ); var flowerHeight = 200; var petalLength = 120; var cylGeom = new THREE.CylinderGeometry( 15, 0, petalLength, 32 ); var flower = new THREE.Object3D(); ///////// // YOUR CODE HERE // add code here to make 24 petals, radiating around the sphere for (var i=0;i<24;i++) { var cylinder = new THREE.Mesh( cylGeom, petalMaterial ); cylinder.rotation.x = 90*Math.PI/180; cylinder.position.z = petalLength/2; var petal = new THREE.Object3D(); petal.position.y = flowerHeight; petal.rotation.y = i*15*Math.PI/180; petal.add( cylinder ); flower.add( petal ); } var stamenMaterial = new THREE.MeshLambertMaterial( { color: 0x333310 } ); var stamen = new THREE.Mesh( new THREE.SphereGeometry( 20, 32, 16 ), stamenMaterial ); stamen.position.y = flowerHeight; // move to flower center flower.add( stamen ); var stemMaterial = new THREE.MeshLambertMaterial( { color: 0x339424 } ); var stem = new THREE.Mesh( new THREE.CylinderGeometry( 10, 10, flowerHeight, 32 ), stemMaterial ); stem.position.y = flowerHeight/2; // move from ground to stamen flower.add( stem ); scene.add( flower ); } function init() { var canvasWidth = 846; var canvasHeight = 494; var canvasRatio = canvasWidth / canvasHeight; // RENDERER renderer = new THREE.WebGLRenderer( { antialias: false } ); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.setSize(canvasWidth, canvasHeight); renderer.setClearColorHex( 0xAAAAAA, 1.0 ); // CAMERA camera = new THREE.PerspectiveCamera( 38, canvasRatio, 1, 10000 ); // CONTROLS cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement); camera.position.set(-200, 400, 20); cameraControls.target.set(0,150,0); fillScene(); } 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 drawHelpers() { if (ground) { Coordinates.drawGround({size:10000}); } if (gridX) { Coordinates.drawGrid({size:10000,scale:0.01}); } if (gridY) { Coordinates.drawGrid({size:10000,scale:0.01, orientation:"y"}); } if (gridZ) { Coordinates.drawGrid({size:10000,scale:0.01, orientation:"z"}); } if (axes) { Coordinates.drawAllAxes({axisLength:200,axisRadius:1,axisTess:50}); } } function animate() { window.requestAnimationFrame(animate); render(); } function render() { var delta = clock.getDelta(); cameraControls.update(delta); if ( effectController.newGridX !== gridX || effectController.newGridY !== gridY || effectController.newGridZ !== gridZ || effectController.newGround !== ground || effectController.newAxes !== axes) { gridX = effectController.newGridX; gridY = effectController.newGridY; gridZ = effectController.newGridZ; ground = effectController.newGround; axes = effectController.newAxes; fillScene(); } renderer.render(scene, camera); } function setupGui() { effectController = { newGridX: gridX, newGridY: gridY, newGridZ: gridZ, newGround: ground, newAxes: axes }; var gui = new dat.GUI(); var h = gui.addFolder("Grid display"); h.add( effectController, "newGridX").name("Show XZ grid"); h.add( effectController, "newGridY" ).name("Show YZ grid"); h.add( effectController, "newGridZ" ).name("Show XY grid"); h.add( effectController, "newGround" ).name("Show ground"); h.add( effectController, "newAxes" ).name("Show axes"); } // this is the main action sequence init(); fillScene(); drawHelpers(); addToDOM(); setupGui(); animate();
4.压扁的花
在上一个程序上的改进,然它看起来更棒一些。
对花瓣继续进行处理,压扁,并且产生一些倾角。最终效果如下:
关键代码
for (var i=0;i<24;i++) { var cylinder = new THREE.Mesh( cylGeom, petalMaterial ); // cylinder.position.y = petalHeight; cylinder.scale.z = 0.25; cylinder.rotation.x = 90*Math.PI/180; //cylinder.rotation.x = -20*Math.PI/180; cylinder.position.z = petalLength/2; var petal = new THREE.Object3D(); petal.add( cylinder ); petal.rotation.x = -20*Math.PI/180; var tmpObject = new THREE.Object3D(); tmpObject.add( petal); tmpObject.rotation.y = i*15*Math.PI/180; tmpObject.position.y = flowerHeight; flower.add( tmpObject ); }