Interactive 3D Graphics是Udacity上的一门图形学公开课(需要×××),最近在学习,在这里记录一下课程的学习进度和作业的答案。
多边形的三角形划分
由于在图形学中,面的表示通常是用三角形来表示,所以要显示多边形的时候,就要将其碎片化。
//////////////////////////////////////////////////////////////////////////////// // Polygon Creation Exercise // Your task is to complete the function PolygonGeometry(sides) // which takes 1 argument: // sides - how many edges the polygon has. // Return the mesh that defines the minimum number of triangles necessary // to draw the polygon. // Radius of the polygon is 1. Center of the polygon is at 0, 0. //////////////////////////////////////////////////////////////////////////////// /*global, THREE, Coordinates, $, document, window*/ var camera, scene, renderer; var windowScale; function PolygonGeometry(sides) { var geo = new THREE.Geometry(); // generate vertices for ( var pt = 0 ; pt < sides; pt++ ) { // Add 90 degrees so we start at +Y axis, rotate counterclockwise around var angle = (Math.PI/2) + (pt / sides) * 2 * Math.PI; var x = Math.cos( angle ); var y = Math.sin( angle ); // YOUR CODE HERE //Save the vertex location - fill in the code geo.vertices.push(new THREE.Vector3(x,y,0)); } // YOUR CODE HERE // Write the code to generate minimum number of faces for the polygon. for ( var fc = 0 ; fc < sides-1; fc++ ) { geo.faces.push( new THREE.Face3( 0, fc, fc+1) ); } // Return the geometry object return geo; } function init() { // Setting up some parameters var canvasWidth = 846; var canvasHeight = 494; var canvasRatio = canvasWidth / canvasHeight; // scene scene = new THREE.Scene(); // Camera: Y up, X right, Z up windowScale = 4; var windowWidth = windowScale * canvasRatio; var windowHeight = windowScale; camera = new THREE.OrthographicCamera( windowWidth / - 2, windowWidth / 2, windowHeight / 2, windowHeight / - 2, 0, 40 ); var focus = new THREE.Vector3( 0,1,0 ); camera.position.x = focus.x; camera.position.y = focus.y; camera.position.z = 10; camera.lookAt(focus); renderer = new THREE.WebGLRenderer({ antialias: false, preserveDrawingBuffer: true}); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.setSize( canvasWidth, canvasHeight ); renderer.setClearColorHex( 0xffffff, 1.0 ); } function showGrids() { // Background grid and axes. Grid step size is 1, axes cross at 0, 0 Coordinates.drawGrid({size:100,scale:1,orientation:"z"}); Coordinates.drawAxes({axisLength:4,axisOrientation:"x",axisRadius:0.02}); Coordinates.drawAxes({axisLength:3,axisOrientation:"y",axisRadius:0.02}); } 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 render() { renderer.render( scene, camera ); } // Main body of the script init(); showGrids(); addToDOM(); var geo = PolygonGeometry(5); var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.FrontSide } ); var mesh = new THREE.Mesh( geo, material ); scene.add( mesh ); render();
运行结果:
在制定位置绘制制定半径的多边形
原先的函数变成了PolygonGeometry(sides,radius, location),只要在生成vertex的时候对坐标进行相应的变换就可已了。
//////////////////////////////////////////////////////////////////////////////// // Polygon Radius Exercise // Your task is to write a function that will take 3 arguments: // sides - how many edges the polygon has. // location - location of the center of the polygon as a THREE.Vector3. // radius - radius of the polygon. // Return the mesh that defines the minimum number of triangles necessary // to draw the polygon. //////////////////////////////////////////////////////////////////////////////// /*global, THREE, Coordinates, $, document, window*/ var camera, scene, renderer; var windowScale; function PolygonGeometry(sides, location, radius) { var geo = new THREE.Geometry(); // generate vertices for ( var pt = 0 ; pt < sides; pt++ ) { // Add 90 degrees so we start at +Y axis, rotate counterclockwise around var angle = (Math.PI/2) + (pt / sides) * 2 * Math.PI; var x = radius*Math.cos(angle) + location.x; var y = radius*Math.sin(angle) + location.y; // Save the vertex location geo.vertices.push( new THREE.Vector3( x, y, 0.0 ) ); } // generate faces for ( var face = 0 ; face < sides-2; face++ ) { // this makes a triangle fan, from the first +Y point around geo.faces.push( new THREE.Face3( 0, face+1, face+2 ) ); } // done: return it. return geo; } function init() { // Setting up some parameters var canvasWidth = 846; var canvasHeight = 494; var canvasRatio = canvasWidth / canvasHeight; // scene scene = new THREE.Scene(); // Camera: Y up, X right, Z up windowScale = 12; var windowWidth = windowScale * canvasRatio; var windowHeight = windowScale; camera = new THREE.OrthographicCamera( windowWidth / - 2, windowWidth / 2, windowHeight / 2, windowHeight / - 2, 0, 40 ); var focus = new THREE.Vector3( 5,5,0 ); camera.position.x = focus.x; camera.position.y = focus.y; camera.position.z = 10; camera.lookAt(focus); renderer = new THREE.WebGLRenderer({ antialias: false, preserveDrawingBuffer: true}); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.setSize(canvasWidth, canvasHeight); renderer.setClearColorHex( 0xffffff, 1.0 ); } function showGrids() { // Background grid and axes. Grid step size is 1, axes cross at 0, 0 Coordinates.drawGrid({size:100,scale:1,orientation:"z"}); Coordinates.drawAxes({axisLength:4,axisOrientation:"x",axisRadius:0.02}); Coordinates.drawAxes({axisLength:3,axisOrientation:"y",axisRadius:0.02}); } 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 render() { renderer.render( scene, camera ); } // Main body of the script init(); showGrids(); addToDOM(); var geo = PolygonGeometry(9, new THREE.Vector3( 5, 5, 0 ), 4); var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.FrontSide } ); var mesh = new THREE.Mesh( geo, material ); scene.add( mesh ); render();
建造楼梯
搭建一个楼梯,最终获得奖杯。
//////////////////////////////////////////////////////////////////////////////// // Staircase exercise // // Your task is to complete the model for simple stairs // // Using the provided sizes and colors, complete the staircase // // and reach the Gold Cup! // //////////////////////////////////////////////////////////////////////////////// /*global, THREE, Coordinates, $, document, window, dat*/ var camera, scene, renderer; var cameraControls, effectController; var clock = new THREE.Clock(); var gridX = false; var gridY = false; var gridZ = false; var axes = false; var ground = true; function createStairs() { // MATERIALS var stepMaterialVertical = new THREE.MeshLambertMaterial( { color: 0xA85F35 } ); var stepMaterialHorizontal = new THREE.MeshLambertMaterial( { color: 0xBC7349 } ); var stepWidth = 500; var stepSize = 200; var stepThickness = 50; // height from top of one step to bottom of next step up var verticalStepHeight = stepSize; var horizontalStepDepth = stepSize*2; var stepHalfThickness = stepThickness/2; // +Y direction is up // Define the two pieces of the step, vertical and horizontal // THREE.CubeGeometry takes (width, height, depth) var stepVertical = new THREE.CubeGeometry(stepWidth, verticalStepHeight, stepThickness); var stepHorizontal = new THREE.CubeGeometry(stepWidth, stepThickness, horizontalStepDepth); var stepMesh; for( var i=0;i<6;i++ ) { stepMesh = new THREE.Mesh( stepVertical, stepMaterialVertical ); // The position is where the center of the block will be put. // You can define position as THREE.Vector3(x, y, z) or in the following way: stepMesh.position.x = 0; // centered at origin stepMesh.position.y = i*verticalStepHeight+verticalStepHeight/2+i*stepThickness; // half of height: put it above ground plane stepMesh.position.z = i*horizontalStepDepth-i*stepThickness; // centered at origin scene.add( stepMesh ); } // Make and position the horizontal part for( var i=0;i<6;i++ ) { stepMesh = new THREE.Mesh( stepHorizontal, stepMaterialHorizontal ); stepMesh.position.x = 0; // Push up by half of horizontal step's height, plus vertical step's height stepMesh.position.y = i*stepThickness + (i+1)*verticalStepHeight+stepHalfThickness; // Push step forward by half the depth, minus half the vertical step's thickness stepMesh.position.z = i*horizontalStepDepth+horizontalStepDepth/2 - i*stepThickness-stepHalfThickness; scene.add( stepMesh ); } } function createCup() { var cupMaterial = new THREE.MeshLambertMaterial( { color: 0xFDD017}); // THREE.CylinderGeometry takes (radiusTop, radiusBottom, height, segmentsRadius) var cupGeo = new THREE.CylinderGeometry( 200, 50, 400, 32 ); var cup = new THREE.Mesh( cupGeo, cupMaterial ); cup.position.x = 0; cup.position.y = 1725; cup.position.z = 1925; scene.add( cup ); cupGeo = new THREE.CylinderGeometry( 100, 100, 50, 32 ); cup = new THREE.Mesh( cupGeo, cupMaterial ); cup.position.x = 0; cup.position.y = 1525; cup.position.z = 1925; scene.add( cup ); } 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( 45, canvasRatio, 1, 40000 ); camera.position.set( -700, 500, -1600 ); // CONTROLS cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement); cameraControls.target.set(0,600,0); // Camera(2) for testing has following values: // camera.position.set( 1225, 2113, 1814 ); // cameraControls.target.set(-1800,180,630); 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 fillScene() { // SCENE scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0x808080, 3000, 6000 ); // 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( -400, 200, -300 ); scene.add(ambientLight); scene.add(light); scene.add(light2); scene.add(camera); if (ground) { Coordinates.drawGround({size:1000}); } if (gridX) { Coordinates.drawGrid({size:1000,scale:0.01}); } if (gridY) { Coordinates.drawGrid({size:1000,scale:0.01, orientation:"y"}); } if (gridZ) { Coordinates.drawGrid({size:1000,scale:0.01, orientation:"z"}); } if (axes) { Coordinates.drawAllAxes({axisLength:300,axisRadius:2,axisTess:50}); } createCup(); var stairs = createStairs(); scene.add(stairs); } // 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, dummy: function() { } }; var gui = new dat.GUI(); gui.add(effectController, "newGridX").name("Show XZ grid"); gui.add( effectController, "newGridY" ).name("Show YZ grid"); gui.add( effectController, "newGridZ" ).name("Show XY grid"); gui.add( effectController, "newGround" ).name("Show ground"); gui.add( effectController, "newAxes" ).name("Show axes"); } init(); addToDOM(); setupGui(); animate();
空间建模
第三题十一个三维建模的题目,需要一些空间想象。
和3dmax的建模类似,在max中建模是交互的,但这里是需要代码来实现。
主要用到了三个函数来创建空间物体:
//Create box boxGeometry = New THREE.CubeGeometry(float width,float,height,float depth); //Create sphere sphereGeometry = New THREE.SphereGeometry(float radius,int segsWidth,int segsHeight); // Create cylinder cylinderGeometry = New THREE.CylinderGeometry(float radius1,float radius1,int height,int segs);
平面图如下:
代码如下
//////////////////////////////////////////////////////////////////////////////// // Drinking Bird Model exercise // // Your task is to complete the model for the drinking bird // // The following forms and sizes should be used: // // Hat: cylinder. color blue (cylinderMaterial) // // Diameter top 80, bottom, full height 80, edge 10 // // Head: sphere, red (sphereMaterial), diameter 104 // // Middle of base: cube, color orange (cubeMaterial), width 77, length 194 // // Feet: cube, color orange, width 6, length 194, height 52 // // Legs: cube, color orange, width 6, length 64, height 386 // // Body: sphere, red, diameter 116 // // Spine: cylinder, blue, diameter 24, length 390 // //////////////////////////////////////////////////////////////////////////////// /*global, THREE, Coordinates, $, document, window, dat*/ var camera, scene, renderer; var cameraControls, effectController; var clock = new THREE.Clock(); var gridX = false; var gridY = false; var gridZ = false; var axes = false; var ground = true; 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( 45, canvasRatio, 1, 40000 ); // CONTROLS cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement); camera.position.set( -480, 659, -619 ); cameraControls.target.set(4,301,92); fillScene(); } // Supporting frame for the bird - base + legs + feet function createSupport() { var cubeMaterial = new THREE.MeshLambertMaterial( { color: 0xF07020 } ); // base var cube; cube = new THREE.Mesh( new THREE.CubeGeometry( 20+64+110, 4, 2*77 ), cubeMaterial ); cube.position.x = -45; // (20+32) - half of width (20+64+110)/2 cube.position.y = 4/2; // half of height cube.position.z = 0; // centered at origin scene.add( cube ); // left foot cube = new THREE.Mesh( new THREE.CubeGeometry( 20+64+110, 52, 6 ), cubeMaterial ); cube.position.x = -45; // (20+32) - half of width (20+64+110)/2 cube.position.y = 52/2; // half of height cube.position.z = 77 + 6/2; // offset 77 + half of depth 6/2 scene.add( cube ); // left leg cube = new THREE.Mesh( new THREE.CubeGeometry( 64, 334+52, 6 ), cubeMaterial ); cube.position.x = 0; // centered on origin along X cube.position.y = (334+52)/2; cube.position.z = 77 + 6/2; // offset 77 + half of depth 6/2 scene.add( cube ); // right foot cube = new THREE.Mesh( new THREE.CubeGeometry( 20+64+110, 52, 6 ), cubeMaterial ); cube.position.x = -45; // (20+32) - half of width (20+64+110)/2 cube.position.y = 52/2; // half of height cube.position.z = -77 + -6/2; // offset 77 + half of depth 6/2 scene.add( cube ); // right leg cube = new THREE.Mesh( new THREE.CubeGeometry( 64, 334+52, 6 ), cubeMaterial ); cube.position.x = 0; // centered on origin along X cube.position.y = (334+52)/2; cube.position.z = -77 + -6/2; // offset 77 + half of depth 6/2 scene.add( cube ); } // Body of the bird - body and the connector of body and head function createBody() { var sphereMaterial = new THREE.MeshLambertMaterial( { color: 0xA00000 } ); var cylinderMaterial = new THREE.MeshLambertMaterial( { color: 0x0000D0 } ); //Create two balls var sphere; sphere = new THREE.Mesh( new THREE.SphereGeometry( 116/2, 32, 16 ), sphereMaterial ); sphere.position.x = 0; sphere.position.y = 160; sphere.position.z = 0; scene.add( sphere ); sphere = new THREE.Mesh( new THREE.SphereGeometry( 104/2, 32, 16 ), sphereMaterial ); sphere.position.x = 0; sphere.position.y = 390+160; sphere.position.z = 0; scene.add( sphere ); //Create a stick var cylinder; cylinder=new THREE.Mesh(new THREE.CylinderGeometry( 12, 12, 390, 32 ), cylinderMaterial); cylinder.position.x = 0; cylinder.position.y = 160+390/2; cylinder.position.z = 0; scene.add( cylinder ); } // Head of the bird - head + hat function createHead() { var sphereMaterial = new THREE.MeshLambertMaterial( { color: 0xA00000 } ); var cylinderMaterial = new THREE.MeshLambertMaterial( { color: 0x0000D0 } ); //Create cap var cylinder; cylinder=new THREE.Mesh(new THREE.CylinderGeometry( 142/2, 142/2, 10, 32 ), cylinderMaterial); cylinder.position.x = 0; cylinder.position.y = 160+390+40+5; cylinder.position.z = 0; scene.add( cylinder ); cylinder=new THREE.Mesh(new THREE.CylinderGeometry( 80/2, 80/2, 70, 32 ), cylinderMaterial); cylinder.position.x = 0; cylinder.position.y = 160+390+40+10+70/2; cylinder.position.z = 0; scene.add( cylinder ); } function createDrinkingBird() { // MODELS // base + legs + feet createSupport(); // body + body/head connector createBody(); // head + hat createHead(); } function fillScene() { // SCENE scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0x808080, 3000, 6000 ); // 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( -400, 200, -300 ); scene.add(ambientLight); scene.add(light); scene.add(light2); scene.add(camera); if (ground) { Coordinates.drawGround({size:1000}); } if (gridX) { Coordinates.drawGrid({size:1000,scale:0.01}); } if (gridY) { Coordinates.drawGrid({size:1000,scale:0.01, orientation:"y"}); } if (gridZ) { Coordinates.drawGrid({size:1000,scale:0.01, orientation:"z"}); } if (axes) { Coordinates.drawAllAxes({axisLength:300,axisRadius:2,axisTess:50}); } createDrinkingBird(); } // 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); 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, dummy: function() { } }; var gui = new dat.GUI(); gui.add(effectController, "newGridX").name("Show XZ grid"); gui.add( effectController, "newGridY" ).name("Show YZ grid"); gui.add( effectController, "newGridZ" ).name("Show XY grid"); gui.add( effectController, "newGround" ).name("Show ground"); gui.add( effectController, "newAxes" ).name("Show axes"); } init(); addToDOM(); setupGui(); animate();
运行效果: