simple 3D 05 (not using PV3D)
PV3Dとかを使わない3D表現その5
フラットシェーディングやってみましたー。
陰面処理とか色の度合いの計算には内積を使ってます。
ライトの位置は固定です。カメラが動いています。
Matrix3D & vector3Dは自作のやつ。
以下のサイトを参考にさせてもらいました。
http://d.hatena.ne.jp/nitoyon/20080620/as_3d_lessen3
/**
* Copyright sakef ( http://wonderfl.net/user/sakef )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/g4Yk
*/
/*
PV3Dとかを使わない3D表現その5
フラットシェーディングやってみましたー。
陰面処理とか色の度合いの計算には内積を使ってます。
ライトの位置は固定です。カメラが動いています。
Matrix3D & vector3Dは自作のやつ。
以下のサイトを参考にさせてもらいました。
http://d.hatena.ne.jp/nitoyon/20080620/as_3d_lessen3
*/
package
{
import net.hires.debug.Stats;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
[SWF(width="465", height="465", backgroundColor="0x000000")]
public class simple3d_05 extends Sprite
{
// 幅・高さ・中心・キューブのサイズ・半径
private static const CENTER_X:int=465 >> 1;
private static const CENTER_Y:int=465 >> 1;
private static const RADIAN:Number=Math.PI / 180;
private static const SIN2:Number=Math.sin(RADIAN * 2);
private static const COS2:Number=Math.cos(RADIAN * 2);
private static const SIZE:int=50;
private static const CAMERA_RADIUS:int=550;
// 上を示すベクトル・中心を示すベクトル・フォーカス
private var UP_VECTOR:MyVector3D=new MyVector3D(0, 1, 0);
private var CENTER_VECTOR:MyVector3D=new MyVector3D(0, 0, 0);
private var FOCUS:Number=400;
// カメラ用
private var camera:MyVector3D;
private var isMouseDown:Boolean;
private var oldX:Number;
private var oldY:Number;
private var targetRot:Number;
private var targetPitch:Number;
private var rot:Number;
private var pitch:Number;
// キューブとライト
private var light:MyVector3D;
private var container:Array;
private var n_cube:int;
// コンストラクタ
public function simple3d_05()
{
// ステージ設定、マウス関係停止。
stage.scaleMode=StageScaleMode.NO_SCALE;
stage.align=StageAlign.TOP_LEFT;
stage.quality=StageQuality.HIGH;
stage.frameRate=60;
mouseChildren=false;
mouseEnabled=false;
// Stats追加
addChild(new Stats());
// 変数の初期化
oldX=oldY=rot=pitch=0;
targetPitch=90;
targetRot=180;
isMouseDown=false;
camera=new MyVector3D(0, 0, FOCUS);
// キューブとライトの初期化
light=new MyVector3D(100, 500, 600);
n_cube=0;
container=[];
for(var i:int=-150; i <= 150; i+=150)
{
for(var j:int=-150; j <= 150; j+=150)
{
for(var k:int=-150; k <= 150; k+=150)
{
container[n_cube]=new MyCube(i, j, k, SIZE, SIZE, SIZE);
n_cube ++;
}
}
}
// 更新用関数追加
addEventListener(Event.ENTER_FRAME, onFrame);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
// 計算更新用関数
private function onFrame(e:Event):void
{
// カメラの移動
if (isMouseDown)
{
targetRot+=(mouseX - oldX) * 0.2;
targetPitch+=(mouseY - oldY) * 0.2;
targetPitch=(targetPitch > -90) ? (targetPitch) : (-90);
targetPitch=(targetPitch < 90) ? (targetPitch) : (90);
oldX=mouseX;
oldY=mouseY;
}
rot+=(targetRot - rot) * 0.1;
pitch+=(targetPitch - pitch) * 0.1;
pitch=(pitch > -90) ? (pitch) : (-90);
pitch=(pitch < 90) ? (pitch) : (90);
camera.x=CAMERA_RADIUS * Math.sin(rot * Math.PI / 180);
camera.y=CAMERA_RADIUS * Math.sin(pitch * Math.PI / 180);
camera.z=CAMERA_RADIUS * Math.cos(rot * Math.PI / 180);
// ビューイング変換行列
var Mview:MyMatrix3D=getViewingTransformMatrix(camera);
// モデリング変換変換行列 (例によって何もしない)
var Mmodel:MyMatrix3D = new MyMatrix3D;
// 座標の計算
for(var i:int=0; i < n_cube; i++) (container[i]as MyCube).render(Mmodel, Mview, light, CENTER_VECTOR);
// ソート
container.sortOn("sortNumber", Array.NUMERIC | Array.DESCENDING);
// 描写
graphics.clear();
for(i=0; i < n_cube; i++) (container[i]as MyCube).draw(graphics, CENTER_X, CENTER_Y, CENTER_VECTOR, FOCUS);
}
// マウスダウン時の関数
private function onMouseDown(e:MouseEvent):void
{
isMouseDown=true;
oldX=mouseX;
oldY=mouseY;
}
// マウスアップ時の関数
private function onMouseUp(e:MouseEvent):void
{
isMouseDown=false;
}
// カメラ位置からビューイング変換行列を返す関数
private function getViewingTransformMatrix(camera:MyVector3D):MyMatrix3D
{
// Center - Eye ベクトルの作成と正規化
var n:MyVector3D=CENTER_VECTOR.subtract(camera);
n.normalize();
// up×nを計算 (外積)
var u:MyVector3D=UP_VECTOR.crossProduct(n);
u.normalize();
// n×uを計算 (外積)
var v:MyVector3D=n.crossProduct(u);
v.normalize();
// dの計算
var dx:Number=-camera.innerProduct(u);
var dy:Number=-camera.innerProduct(v);
var dz:Number=-camera.innerProduct(n);
// ビューイング変換行列
var Mview:MyMatrix3D=new MyMatrix3D(u.x, u.y, u.z, dx, v.x, v.y, v.z, dy, n.x, n.y, n.z, dz, 0, 0, 0, 1);
return Mview;
}
}
}
/*
キューブクラス
*/
import flash.display.Graphics;
import flash.utils.Dictionary;
final class MyCube
{
public var position:MyVector3D;
public var sortNumber:Number;
private var points:Array;
private var eye_Coord_points:Array;
private var eye_Coord_position:MyVector3D;
private var eye_Coord_light:MyVector3D;
// コンストラクタ
public function MyCube(x:Number=0, y:Number=0, z:Number=0, widthX:Number=100, widthY:Number=100, widthZ:Number=100)
{
position=new MyVector3D(x, y, z);
sortNumber=0;
var wx:Number=widthX >> 1;
var wy:Number=widthY >> 1;
var wz:Number=widthZ >> 1;
points=[new MyVector3D(wx, -wy, wz), new MyVector3D(wx, -wy, -wz), new MyVector3D(-wx, -wy, -wz), new MyVector3D(-wx, -wy, wz), new MyVector3D(wx, wy, wz), new MyVector3D(wx, wy, -wz), new MyVector3D(-wx, wy, -wz), new MyVector3D(-wx, wy, wz)];
eye_Coord_points=[];
}
// 座標の更新
public function render(Mmodel:MyMatrix3D, Mview:MyMatrix3D, light:MyVector3D, center:MyVector3D):void
{
// 各座標を更新
position=Mmodel.productVector(position);
for(var i:int=0; i < 8; i++) points[i]=Mmodel.productVector(points[i]);
// アイ座標系を計算
eye_Coord_position=Mview.productVector(position);
for(i=0; i < 8; i++) eye_Coord_points[i]=Mview.productVector(points[i]);
// ライトの座標を愛座標系に変換して保存
eye_Coord_light=Mview.productVector(light);
// ソート用の変数の更新
sortNumber= center.distance(eye_Coord_position);
}
public function draw(g:Graphics, centerX:Number, centerY:Number, center:MyVector3D, focus:Number):void
{
// 各平面を準備 (Bottom, Top, Front, Back, Right, Left)
var planes:Array=[];
planes[0]=[eye_Coord_points[0], eye_Coord_points[1], eye_Coord_points[2], eye_Coord_points[3]];
planes[1]=[eye_Coord_points[4], eye_Coord_points[7], eye_Coord_points[6], eye_Coord_points[5]];
planes[2]=[eye_Coord_points[0], eye_Coord_points[3], eye_Coord_points[7], eye_Coord_points[4]];
planes[3]=[eye_Coord_points[1], eye_Coord_points[5], eye_Coord_points[6], eye_Coord_points[2]];
planes[4]=[eye_Coord_points[0], eye_Coord_points[4], eye_Coord_points[5], eye_Coord_points[1]];
planes[5]=[eye_Coord_points[3], eye_Coord_points[2], eye_Coord_points[6], eye_Coord_points[7]];
// 各面のソート (参考にしたサイト様:http://d.hatena.ne.jp/nitoyon/20080620/as_3d_lessen3)
var zBuffer:Dictionary=new Dictionary();
for(var i:int=0; i < 6; i++) zBuffer[planes[i]]=(planes[i][0].z + planes[i][1].z + planes[i][2].z + planes[i][3].z) * 0.25;
planes.sort(function(a:Array, b:Array):Number{return zBuffer[a] - zBuffer[b];});
// 描写
var x:Number=eye_Coord_position.x;
var y:Number=eye_Coord_position.y;
var z:Number=eye_Coord_position.z;
for(i=0; i < 6; i++)
{
// 面の外積を計算
var plane:Array=planes[i];
var P:MyVector3D=(plane[3]as MyVector3D).subtract(plane[0]as MyVector3D);
var Q:MyVector3D=(plane[1]as MyVector3D).subtract(plane[0]as MyVector3D);
var newVec:MyVector3D=P.crossProduct(Q);
newVec.normalize();
// カメラ位置までのベクトルを計算
var cameraVec:MyVector3D = center.subtract(eye_Coord_position);
cameraVec.normalize();
// 内積とって隠面処理
var isFace:Number = cameraVec.innerProduct(newVec);
if(isFace > 0)
{
// ライトへのベクトルを計算
var lightVec:MyVector3D=eye_Coord_light.subtract(eye_Coord_position);
lightVec.normalize();
// 内積から色の度合いを計算
var degree:Number=newVec.innerProduct(lightVec);
degree=(degree < 0.1) ? (0.1) : (degree);
var color:uint=(0x00)<<16 || 0xFF*degree<<8 || 0x00;
// 3D→2D変換 & 描写
var point:MyVector3D=plane[0];
var scale:Number=focus / point.distance(center);
var screenX:Number=(point.x + x) * scale + centerX;
var screenY:Number=-(point.y + y) * scale + centerY;
g.beginFill(color, 1);
g.moveTo(screenX, screenY);
for(var j:int=1; j < 4; j++)
{
point=plane[j];
scale=focus / point.distance(center);
screenX=(point.x + x) * scale + centerX;
screenY=-(point.y + y) * scale + centerY;
g.lineTo(screenX, screenY);
}
g.endFill();
}
}
}
}
/*
行列演算用クラス
*/
final class MyMatrix3D
{
// 行列の要素
public var v11:Number, v12:Number, v13:Number, v14:Number;
public var v21:Number, v22:Number, v23:Number, v24:Number;
public var v31:Number, v32:Number, v33:Number, v34:Number;
public var v41:Number, v42:Number, v43:Number, v44:Number;
// コンストラクタ
public function MyMatrix3D(v11:Number=1, v12:Number=0, v13:Number=0, v14:Number=0,
v21:Number=0, v22:Number=1, v23:Number=0, v24:Number=0,
v31:Number=0, v32:Number=0, v33:Number=1, v34:Number=0,
v41:Number=0, v42:Number=0, v43:Number=0, v44:Number=1)
{
this.v11=v11; this.v12=v12; this.v13=v13; this.v14=v14;
this.v21=v21; this.v22=v22; this.v23=v23; this.v24=v24;
this.v31=v31; this.v32=v32; this.v33=v33; this.v34=v34;
this.v41=v41; this.v42=v42; this.v43=v43; this.v44=v44;
}
// 行列との積 (自分をA、引数をBとするとA*B)
public function productMatrix(m:MyMatrix3D):MyMatrix3D
{
var newMat:MyMatrix3D=new MyMatrix3D();
newMat.v11=v11 * m.v11 + v12 * m.v21 + v13 * m.v31 + v14 * m.v41;
newMat.v12=v11 * m.v12 + v12 * m.v22 + v13 * m.v32 + v14 * m.v42;
newMat.v13=v11 * m.v13 + v12 * m.v23 + v13 * m.v33 + v14 * m.v43;
newMat.v14=v11 * m.v14 + v12 * m.v24 + v13 * m.v34 + v14 * m.v44;
newMat.v21=v21 * m.v11 + v22 * m.v21 + v23 * m.v31 + v24 * m.v41;
newMat.v22=v21 * m.v12 + v22 * m.v22 + v23 * m.v32 + v24 * m.v42;
newMat.v23=v21 * m.v13 + v22 * m.v23 + v23 * m.v33 + v24 * m.v43;
newMat.v24=v21 * m.v14 + v22 * m.v24 + v23 * m.v34 + v24 * m.v44;
newMat.v31=v31 * m.v11 + v32 * m.v21 + v33 * m.v31 + v34 * m.v41;
newMat.v32=v31 * m.v12 + v32 * m.v22 + v33 * m.v32 + v34 * m.v42;
newMat.v33=v31 * m.v13 + v32 * m.v23 + v33 * m.v33 + v34 * m.v43;
newMat.v34=v31 * m.v14 + v32 * m.v24 + v33 * m.v34 + v34 * m.v44;
newMat.v41=v41 * m.v11 + v42 * m.v21 + v43 * m.v31 + v44 * m.v41;
newMat.v42=v41 * m.v12 + v42 * m.v22 + v43 * m.v32 + v44 * m.v42;
newMat.v43=v41 * m.v13 + v42 * m.v23 + v43 * m.v33 + v44 * m.v43;
newMat.v44=v41 * m.v14 + v42 * m.v24 + v43 * m.v34 + v44 * m.v44;
return newMat;
}
// ベクトルとの積
public function productVector(v:MyVector3D):MyVector3D
{
var newVec:MyVector3D=new MyVector3D();
newVec.x=v11 * v.x + v12 * v.y + v13 * v.z + v14;
newVec.y=v21 * v.x + v22 * v.y + v23 * v.z + v24;
newVec.z=v31 * v.x + v32 * v.y + v33 * v.z + v34;
return newVec;
}
}
/*
ベクトル演算用クラス
*/
final class MyVector3D
{
// x、y、z座標を保存する変数
public var x:Number;
public var y:Number;
public var z:Number;
public var w:Number;
// コンストラクタ
public function MyVector3D(x:Number=0, y:Number=0, z:Number=0)
{
this.x=x;
this.y=y;
this.z=z;
this.w=1;
}
// 距離
public function distance(v:MyVector3D):Number
{
return Math.sqrt((v.x - x) * (v.x - x) + (v.y - y) * (v.y - y) + (v.z - z) * (v.z - z));
}
// 差
public function subtract(v:MyVector3D):MyVector3D
{
var newVec:MyVector3D=new MyVector3D();
newVec.x=x - v.x;
newVec.y=y - v.y;
newVec.z=z - v.z;
return newVec;
}
// 正規化
public function normalize():void
{
var len:Number=Math.sqrt(x * x + y * y + z * z);
x/=len;
y/=len;
z/=len;
}
// 内積
public function innerProduct(v:MyVector3D):Number
{
return (x * v.x + y * v.y + z * v.z);
}
// 外積(クロス積) (自分をA、引数をBとすると、A×Bを計算)
public function crossProduct(v:MyVector3D):MyVector3D
{
var newVec:MyVector3D=new MyVector3D();
newVec.x=y * v.z - z * v.y;
newVec.y=z * v.x - x * v.z;
newVec.z=x * v.y - y * v.x;
return newVec;
}
}