Lighting a 3D object in Flash, pt 1
This is the first part in a tutorial on using a normal map to light a sphere in Flash.
First of all, we need some object to light.. for this I revisited the Sphere I did earlier, and improved the method of its construction, using radians instead of degrees, and also generating the indices in the same loop as the vertices. First, here is what it looks like with a flat, outlined fill…
flash 10 required
Since I just installed the snazzy CodeColorer plugin, here’s the code…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | package com.dafishinsea.tutorials.normalmap { import flash.display.BitmapData; import flash.display.Sprite; import flash.display.TriangleCulling; import flash.events.Event; import flash.geom.Matrix3D; import flash.geom.PerspectiveProjection; import flash.geom.Utils3D; import flash.geom.Vector3D; import flash.text.TextField; /** * procedural model of a Pumpkin to demonstrate use of normal map */ [SWF(backgroundColor="0x000000", width="800", height="600", frameRate="20")] public class NormalMap1 extends Sprite { private var vertices:Vector.<Number>; private var projectedVerts:Vector.<Number>; private var numverts:int; private var indices:Vector.<int>; private var uvts:Vector.<Number>; private var texture:BitmapData; private var perspective:PerspectiveProjection; private var projMatrix:Matrix3D; private var sphereRadius:Number; private var sphereCenter:Vector3D; private var rows:int;//number of times the sphere is sliced to form segments private var cols:int; private const PI:Number = Math.PI;//half revolution in radians private const HALFPI:Number = Math.PI/2;//1/4 revolution in radians private const TWOPI:Number = 2*Math.PI;//full revolution in radians private var canvas:Sprite; private var r:Number = 0; //the number of segments = num slices - 1 //this is the same as lines of longitude //thus there are twice as many as horizontal slices public function NormalMap1() { init(); createMesh(); addEventListener(Event.ENTER_FRAME, onEnterFrame); addEventListener(Event.RENDER, render); //render(); } private function onEnterFrame(event:Event):void { r = r + 0.2; prerender(); stage.invalidate(); } /** * initialize common properties */ private function init():void { perspective = root.transform.perspectiveProjection; texture = new BitmapData(800,800,false,0xCCCCCC); sphereRadius = 100; //sphereCenter = new Vector3D(stage.stageWidth/2, stage.stageHeight/2, 200); cols = 40; rows = 20; numverts = (cols+1)*(rows+1); vertices = new Vector.<Number>(numverts*3);//x,y,z for each vertex projectedVerts = new Vector.<Number>(numverts*3); uvts = new Vector.<Number>(numverts*3); indices = new Vector.<int>(); //we need to work around the fact that Utils3D.projectVectors disregards the projection center //of the projectionMatrix it is passed .. it always projects around 0,0 //so we must render things on a canvas centered on stage canvas = new Sprite(); canvas.x = stage.stageWidth/2; canvas.y = stage.stageHeight/2; addChild(canvas); } /** * create the mesh of the Sphere */ private function createMesh():void { var lon_incr:Number = TWOPI/cols; var lat_incr = PI/rows; var lon:Number = 0;//angle of rotation around the y axis, *in radians* var lat:Number = 0;//angle of rotation around the x axis var x:Number, y:Number, z:Number; var vnum:int = 0; var ind:int = 0; //a full rotation is PI radians for(var h:int = 0; h <= rows; ++h) { y = sphereRadius*Math.cos(lat);//need to shift angle downwards by 1/4 rev for(var v:int = 0; v <= cols; ++v) { x = sphereRadius*Math.cos(lon)*Math.sin(lat); z = sphereRadius*Math.sin(lon)*Math.sin(lat);//seen from above, z = y //add vertex triplet vertices[vnum] = x; vertices[vnum+1] = y; vertices[vnum+2] = z; vnum+=3; //add indices if(h < rows && v < cols){ indices.push(ind, ind+1, ind + cols+1); indices.push(ind + cols+1, ind+1, ind + cols + 2); } ind+=1; lon += lon_incr; } lat += lat_incr; } } /** * prerender the sphere -- project vertices using rotated matrix */ private function prerender():void { projMatrix = perspective.toMatrix3D(); projMatrix.prependTranslation(0,0,400); projMatrix.prependRotation(r, new Vector3D(0,1,0)); Utils3D.projectVectors(projMatrix, vertices, projectedVerts,uvts); } /** * render */ private function render(event:Event):void { canvas.graphics.clear(); canvas.graphics.beginFill(0xFFFFFF); canvas.graphics.lineStyle(1,0xcccccc,1); canvas.graphics.drawTriangles(projectedVerts, indices, null /* uvts */, TriangleCulling.POSITIVE); canvas.graphics.endFill(); } } } |
And if you actually want to download the code, here’s the link:
Da Code
(@GitHub)
February 14th, 2010 at 7:47 am
[...] as optimize the performance using the normal map + PixelBender normal map shader that I used in my normal map tutorial. Here is the result – use the sliders to change the direction of the [...]