Gouraud shading using GradientFill Gouraud shading using GradientFill crunchy forked:0favorite:2lines:266license : MIT License modified : 2009-10-09 04:25:58 Embed Tweet // Gouraud shading using GradientFill package { import __AS3__.vec.Vector; import flash.display.BitmapData; import flash.display.GradientType; import flash.display.Graphics; import flash.display.GraphicsEndFill; import flash.display.GraphicsGradientFill; import flash.display.GraphicsPath; import flash.display.GraphicsPathCommand; import flash.display.GraphicsSolidFill; import flash.display.GraphicsTrianglePath; import flash.display.IGraphicsData; import flash.display.Shape; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.geom.Matrix3D; import flash.geom.PerspectiveProjection; import flash.geom.Point; import flash.geom.Utils3D; import flash.geom.Vector3D; import flash.utils.getTimer; [SWF(width="800", height="800", frameRate="60", backgroundColor="#0000FF")] public class GradientTesting extends Sprite { static private const MAX_X:int = 20; static private const MAX_Y:int = 20; static private const MULT:Number = 50; static private const MOVE_SIN:int = 0; static private const MOVE_PERLIN:int = 1; static private const NUM_MOVE:int = 2; public var shape:Shape = new Shape; public var movementType_:int = MOVE_SIN; public var lNW_:Vector3D; public var vsW_:Vector.<Number> = new Vector.<Number>; public var vNW_:Vector.<Vector3D> = new Vector.<Vector3D>; public var light_:Vector.<Number> = new Vector.<Number>; public var vsS_:Vector.<Number> = new Vector.<Number>; public var uvt_:Vector.<Number> = new Vector.<Number>; public var index_:Vector.<int> = new Vector.<int>; public function GradientTesting() { addChild(shape); lNW_ = new Vector3D(1, 1, 1); lNW_.normalize(); var faces:Vector.<Face> = new Vector.<Face>; for (var y:Number = 0; y <= MAX_Y; y++) { for (var x:Number = 0; x <= MAX_X; x++) { vsW_.push((x - MAX_X / 2) * MULT, (y - MAX_Y / 2) * MULT, -1000); uvt_.push(x / MAX_X, y / MAX_Y, 0); if (x != MAX_X && y != MAX_Y) { faces.push(new Face(y * (MAX_Y + 1) + x, y * (MAX_Y + 1) + (x + 1), (y + 1) * (MAX_Y + 1) + x)); faces.push(new Face(y * (MAX_Y + 1) + (x + 1), (y + 1) * (MAX_Y + 1) + (x + 1), (y + 1) * (MAX_Y + 1) + x)); } } } faces.sort(faceCompFunc); for each (var f:Face in faces) { index_.push(f.index_[0], f.index_[1], f.index_[2]); } addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage); } private function onAddedToStage(event:Event):void { shape.x = stage.stageWidth / 2; shape.y = stage.stageHeight / 2; addEventListener(Event.ENTER_FRAME, onEnterFrame); stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); stage.addEventListener(Event.MOUSE_LEAVE, onMouseLeave); stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); } private function onRemovedFromStage(event:Event):void { removeEventListener(Event.ENTER_FRAME, onEnterFrame); stage.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); stage.removeEventListener(Event.MOUSE_LEAVE, onMouseLeave); stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); } private function onMouseDown(event:Event):void { movementType_ = (movementType_ + 1) % NUM_MOVE; } private function onMouseLeave(event:Event):void { lNW_ = new Vector3D(1, 1, 1); lNW_.normalize(); } private function onMouseMove(event:MouseEvent):void { lNW_ = new Vector3D(-event.stageX / stage.stageWidth * 6 + 3, -event.stageY / stage.stageHeight * 6 + 3, 1); lNW_.normalize(); } private function faceCompFunc(f0:Face, f1:Face):Number { return minDistSq(f0) - minDistSq(f1); } private function minDistSq(f:Face):Number { var minDist:Number = Number.MAX_VALUE; for each (var i:int in f.index_) { var dist:Number = vsW_[i * 3] * vsW_[i * 3] + vsW_[i * 3 + 1] * vsW_[i * 3 + 1]; if (dist < minDist) { minDist = dist; } } return minDist; } private function onEnterFrame(event:Event):void { switch(movementType_) { case MOVE_SIN: moveSin(); break; case MOVE_PERLIN: movePerlin(); break; } update(); draw(); } public function moveSin():void { var xdiv:Number = 4 + Math.sin(getTimer() / 3000) * 2; var ydiv:Number = 4 + Math.cos(getTimer() / 3000) * 2; var h:Number; for (var y:Number = 0; y <= MAX_Y; y++) { for (var x:Number = 0; x <= MAX_X; x++) { h = 1000 + Math.sin(x/xdiv - getTimer() / 500) * 100 + Math.sin(y/ydiv - getTimer() / 500) * 100; vsW_[(y * (MAX_X + 1) + x) * 3 + 2] = h; } } } private var perlinBitmapData:BitmapData = new BitmapData(MAX_X + 1, MAX_Y + 1, false); public function movePerlin():void { var offsets:Array = [ new Point(-getTimer() / 100, -getTimer() / 100), new Point(getTimer() / 100, getTimer() / 100), new Point(-getTimer() / 200, -getTimer() / 200)]; perlinBitmapData.perlinNoise(MAX_X, MAX_Y, 3, 1, false, true, 7, true, offsets); for (var y:Number = 0; y <= MAX_Y; y++) { for (var x:Number = 0; x <= MAX_X; x++) { var h:Number = perlinBitmapData.getPixel(x, y) & 0xFF; h = 800 + h * 2; vsW_[(y * (MAX_X + 1) + x) * 3 + 2] = h; } } } public function update():void { vNW_.length = 0; var n:int; for (n = 0; n < vsW_.length / 3; n++) { vNW_.push(new Vector3D(0, 0, 0, 0)); } var faceNW:Vector3D = new Vector3D; for (var i:int = 0; i < index_.length; i += 3) { computeNormal( vsW_[index_[i] * 3], vsW_[index_[i] * 3 + 1], vsW_[index_[i] * 3 + 2], vsW_[index_[i + 1] * 3], vsW_[index_[i + 1] * 3 + 1], vsW_[index_[i + 1] * 3 + 2], vsW_[index_[i + 2] * 3], vsW_[index_[i + 2] * 3 + 1], vsW_[index_[i + 2] * 3 + 2], faceNW); vNW_[index_[i]] = vNW_[index_[i]].add(faceNW); vNW_[index_[i + 1]] = vNW_[index_[i + 1]].add(faceNW); vNW_[index_[i + 2]] = vNW_[index_[i + 2]].add(faceNW); } light_.length = 0; for (n = 0; n < vNW_.length; n++) { vNW_[n].normalize(); light_.push(Math.max(0, Math.min(1, lNW_.dotProduct(vNW_[n])))); } } public function draw():void { // create matrix var m:Matrix3D = new Matrix3D(); var pp:PerspectiveProjection = new PerspectiveProjection(); pp.focalLength = 3; pp.fieldOfView = 48; m = pp.toMatrix3D(); // compute screen coords vsS_.length = 0; Utils3D.projectVectors(m, vsW_, vsS_, uvt_); // draw triangles var graphicsData:Vector.<IGraphicsData> = new Vector.<IGraphicsData>; //graphicsData.push(new GraphicsTrianglePath(vsS_, index_, uvt_)); for (var i:int = 0; i < index_.length; i += 3) { drawShadedTriangle(graphicsData, new Vector3D(vsS_[index_[i] * 2], vsS_[index_[i] * 2 + 1], 0), new Vector3D(vsS_[index_[i + 1] * 2], vsS_[index_[i + 1] * 2 + 1], 0), new Vector3D(vsS_[index_[i + 2] * 2], vsS_[index_[i + 2] * 2 + 1], 0), light_[index_[i]], light_[index_[i + 1]], light_[index_[i + 2]]); } var g:Graphics = shape.graphics; g.clear(); //g.lineStyle(1, 0x00FF00); g.drawGraphicsData(graphicsData); } static public function computeNormal( p0x:Number, p0y:Number, p0z:Number, p1x:Number, p1y:Number, p1z:Number, p2x:Number, p2y:Number, p2z:Number, result:Vector3D):void { result.x = (p1y - p0y) * (p2z - p0z) - (p1z - p0z) * (p2y - p0y); result.y = (p1z - p0z) * (p2x - p0x) - (p1x - p0x) * (p2z - p0z); result.z = (p1x - p0x) * (p2y - p0y) - (p1y - p0y) * (p2x - p0x); } static private const SHADE_SIZE:Number = 819.2; // 2^13 / 10 = 819.2 static private const fillType:String = GradientType.LINEAR; static private const colors:Array = [0x000000, 0x000000]; static private const alphas:Array = [1, 0]; static private const ratios:Array = [0x00, 0xFF]; static private const spreadMethod:String = SpreadMethod.PAD; static private const commands:Vector.<int> = Vector.<int>( [GraphicsPathCommand.MOVE_TO, GraphicsPathCommand.LINE_TO, GraphicsPathCommand.LINE_TO, GraphicsPathCommand.LINE_TO]); private function drawShadedTriangle( graphicsData:Vector.<IGraphicsData>, p0S:Vector3D, p1S:Vector3D, p2S:Vector3D, s0:Number, s1:Number, s2:Number):void { var s0s1:Boolean = s0 - s1 < .001 && s0 - s1 > -.001; var s1s2:Boolean = s1 - s2 < .001 && s1 - s2 > -.001; if (s0s1 && s1s2) { graphicsData.push(new GraphicsSolidFill(0x000000, 1 - s0)); } else { if (s0s1) { var pTempS:Vector3D = p0S; var sTemp:Number = s0; p0S = p1S; s0 = s1; p1S = p2S; s1 = s2; p2S = pTempS; s2 = sTemp; } var tempM:Matrix = new Matrix( p1S.x - p0S.x, p1S.y - p0S.y, p2S.x - p0S.x, p2S.y - p0S.y, p0S.x, p0S.y); var a11:Number = SHADE_SIZE * 2 * (s1 - s0); var a21:Number = SHADE_SIZE * 2 * (s2 - s0); var a31:Number = -SHADE_SIZE + SHADE_SIZE * 2 * s0; var m:Matrix = new Matrix(1 / a11, 0, -a21 / a11, 1, -a31 / a11, 0); m.concat(tempM); graphicsData.push(new GraphicsGradientFill(fillType, colors, alphas, ratios, m, spreadMethod)); } graphicsData.push(new GraphicsPath(commands, Vector.<Number>([p0S.x, p0S.y, p1S.x, p1S.y, p2S.x, p2S.y, p0S.x, p0S.y]))); graphicsData.push(new GraphicsEndFill); } } } import __AS3__.vec.Vector; class Face { public var index_:Vector.<int>; public function Face(i0:int, i1:int, i2:int):void { index_ = Vector.<int>([i0, i1, i2]); } } Code Fullscreen Preview Fullscreen tlecoz oreore gouraud lighting shading Vector3D add MouseEvent.MOUSE_LEAVE Vector MouseEvent.REMOVED_FROM_STAGE dotProduct MouseEvent.MOUSE_MOVE Number.MAX_VALUE MouseEvent.MOUSE_DOWN MouseEvent.ENTER_FRAME addEventListener sort normalize stageY stageX concat graphics MouseEvent.ADDED_TO_STAGE removeEventListener Math.sin