【学习笔记】3D图形核心基础精炼版-12:stage3D实战-动态阴影 shadow mapping 和范例工程4

目的:
    物体投影在另一个物体身上,而另一个物体可能是平的,但大多数都是不平的多边形物体,这里考虑的是后者,这样可以适用于大多数场合的投影。

预览效果:
【学习笔记】3D图形核心基础精炼版-12:stage3D实战-动态阴影 shadow mapping 和范例工程4_第1张图片

原理:

    这里使用的是shadow mapping方式,其原理如下:

       1、将场景的深度值预先渲染到 以光源位置为原点、光线发射方向为观察方向的投影坐标系中,形成深度纹理。

       2、再次渲染场景的过程中,将每个片断(像素)变换到前述眼坐标系中,并缩放到[0,1]的范围内以便查询纹理。

       3、以较暗的光照绘制场景

       4、以当前片断在眼坐标中的S、T坐标查询深度纹理获得深度值,将此深度值与当前片断的R坐标进行比较,若R坐标大于深度值,则当前片断在阴影中;否则当前片断受光照。

转换为stage3D后的流程

       1、获得深度图:将要投影的模型以光点的视角绘制成图,.xyz取值[0,1],储存深度

       2、绘制被投影物体:

                    -- 顶点仍然要以光点的视角转换(P),以便计算(但输出仍是实际相机视角输出)

                    -- 取得光点视角的点对应的深度图上的深度,通过将光视角上的点P转为设备坐标(范围[-1,1])P2,再转为深度图的坐标P3,

                        这样就相当于计算出了相对于深度图的正确UV坐标,这个时候可以用该UV去取得深度图的纹理了

                    -- 取得深度图中的该点对应P的深度值与该点P对比,大了就可以涂黑了

鼠绘图说明:
1、这个是以灯光视角查看物体和场地的效果预览,黄色是灯光,黑色是投影物体,蓝色是被投影的场地。
【学习笔记】3D图形核心基础精炼版-12:stage3D实战-动态阴影 shadow mapping 和范例工程4_第2张图片
2、以相机视角查看效果预览,这里可以看得出理论上应该是投影成这个样子的(只是随手一画,并不严谨,参考即可)
【学习笔记】3D图形核心基础精炼版-12:stage3D实战-动态阴影 shadow mapping 和范例工程4_第3张图片

3、我们以灯光视角来绘制投影物体(黑),但只储存深度信息(z轴),所以越远越白,越近越黑,储存到纹理上

【学习笔记】3D图形核心基础精炼版-12:stage3D实战-动态阴影 shadow mapping 和范例工程4_第4张图片
4、绘制被投影物体,要拿刚才那个深度图纹理对比,取得的点必须正确,如果取得红色点的话就是错的了,就会像下图那样显示在物体中间了,其实应该只显示一半的
【学习笔记】3D图形核心基础精炼版-12:stage3D实战-动态阴影 shadow mapping 和范例工程4_第5张图片



关键代码--获得深度图:

"注意,由于我们这里绘制的图是越离光近越黑越接近0,越远越白越接近1,所以默认让画布是白的表示其他地图最远,否则计算结果不正确"
context3d.clear(1,1,1);

// vc0 MLP
finalMatrix.identity(); // 归零
finalMatrix.append(modelMatrix); // M 投影物体的模型矩阵 
var lightMatrix:Matrix3D = new Matrix3D(); // 计算光点矩阵L(以光点为视角)
lightMatrix.appendTranslation(lightPos.x,lightPos.y,lightPos.z);
finalMatrix.append(lightMatrix.clone());
finalMatrix.append(projectionMatrix); // P矩阵

// fc0 用到两个常量 fc0.x=zFar  和  fc0.w 使生成的纹理.w=1
context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,0,Vector.([zFar,0,0,1]));


// vertex shader
"m44 vt0 va0 vc0 \n" // 最终输出点vt0 = 原始顶点*MLP
"mov v1 vt0\n" + // 将它传输给片段着色器使用
"mov op vt0" // 将它输出

// fragment shader
"mov ft0.xyz v1.zzz\n" + // 让图片着色统一是z,取值范围 [0,zFar]
"mov ft0.w fc0.w\n" + // 图片色彩w = 1 即透明度
"div ft0.xyz ft0.xyz fc0.x\n" + // ft0.xyz = ft0.xyz / zFar 让取值变为范围[0,1]
"mov oc,ft0" // 输出,由于颜色值根据深度归为0~1,所以越远越接近1越白,越近越黑

// 获得图(由于这里不需要再在CPU上处理,直接GPU上处理即可,所以使用setRenderToTexture)
context3d.setRenderToTexture(texShadow,false,0,0); // 设置即将要渲染到的位图
context3d.clear(0,0,0); // 填色黑色
lightViewEntity(); // 渲染物体(绘制深度图)
context3d.setRenderToBackBuffer(); // 设置渲染到后台缓冲区(配合setRenderToTexture使用,用后恢复正常)


// 如果必须要到CPU上处理,还可以这样获得位图图源到bdShadow:BitmapData,
context3d.clear(0,0,0);
lightViewEntity();
context3d.drawToBitmapData(bdShadow);
texShadow.uploadFromBitmapData(bdShadow);

关键代码--渲染被投影物体:

// 灯光坐标点
private var lightPos:Vector3D = new Vector3D(0,0,-15,0);

// vc0 MVP 相机视角最终矩阵
finalMatrix.identity();
finalMatrix.append(floorMatrix);
finalMatrix.append(viewMatrix);
finalMatrix.append(projectionMatrix);
context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,finalMatrix,true);

// vc4 MLP 灯光视角最终矩阵
finalMatrix.identity();
finalMatrix.append(floorMatrix);
var lightMatrix:Matrix3D = new Matrix3D();
lightMatrix.appendTranslation(lightPos.x,lightPos.y,lightPos.z);
finalMatrix.append(lightMatrix.clone());
finalMatrix.append(projectionMatrix);
context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,4,finalMatrix,true);

// -- 设置状态机当前的VB的坐标 到 va0 ,因为只有XYZ,所以偏移是0,长度是3
context3d.setVertexBufferAt(0,modelVbFloor,0,Context3DVertexBufferFormat.FLOAT_3);

// -- 设置状态机当前的VB的UV纹理坐标 到 va2
context3d.setVertexBufferAt(2,modelVbFloor,6,Context3DVertexBufferFormat.FLOAT_2);

// fc2 = x=zFar y=0.5(用于辅助计算)
context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,2,Vector.([zFar,0.5,1,1]));


// vertex shader
"m44 vt0 va0 vc0 \n" + // vt0 = 原始顶点*MVP 相机视角
"m44 vt1 va0 vc4\n" + // vt1 = 原始顶点*MLP 光视角
"mov v1 vt1\n"+ // 灯光视角后的顶点传输到片段着色器
"mov v1 va2\n"+ // 被投影物体的UV
"mov op vt0\n" // 输出顶点(这个仍然是相机视角的)

// fragment shader
"tex ft1 v1 fs1<2d,linear,repeat,nomip>\n"+ // 被投影物的纹理采样
"div ft2.xy v0.xy v0.zz\n"+ // 转为设备空间,x和y的取值范围是[-1,1] 就是三维点转屏幕上的二维点
"mul ft2.xy ft2.xy fc2.y\n"+ // *0.5 为适应贴图坐标系([0,1]),我们将[-1,1]转为[0,1],转换方法是new=old*0.5+0.5,然后Y取反
"add ft2.xy ft2.xy fc2.y\n"+ // +0.5
"neg ft2.y ft2.y\n"+ // Y取反,因为屏幕坐标系Y是+往上,贴图坐标中Y是-往上,最终计算出来的可以说是UV坐标

"tex ft3 ft2.xy fs0<2d,linear,repeat,nomip>\n"+ // 根据计算出来的UV坐标和深度图纹理取得纹理
"mul ft5.z ft3.z fc2.x\n"+ // z扩大zFar,让原来的0-1变为0-zFar
"slt ft5.w ft5.z v0.z\n"+ // 如果深度图中的深度 小于 (顶点xMLP)的深度的话就是1,否则就是0 也就是说影子时1,非影子0

"mul ft5.w ft5.w fc2.y\n" + // 作出效果,让 影子1,非影子0 转为影子0.5,非影子1,这样直接与原始颜色相乘让影子部分的颜色等于原来的一半
"neg ft5.w ft5.w\n" +
"add ft5.w ft5.w fc2.y\n" +
"add ft5.w ft5.w fc2.y\n" +
"mul ft1 ft1 ft5.w\n"+

"mov oc ft1\n" // oc ft1

注意点:

   1、绘制深度图时由于我们这里绘制的图是越离光近越黑越接近0,越远越白越接近1,所以默认让画布是白的表示其他地图最远,否则会认为黑色部分都是遮挡部分

   2、在绘制被投影物体的时候,如何转换坐标来对比,这里还是要临时转到灯光视角,然后计算到该视角中顶点对应的深度图纹理坐标,原理:

                 --- pMLP = 原始顶点 * 灯光最终矩阵 = p * MLP (这里的L就是V,只不过和灯光一个位置和角度了)

                 --- SB(pMLP) = pMLP.xy / pMLP.zz  (就是x和y分别除以z,这样就转为屏幕坐标系了,就是中心点为0,0,左边框x=-1,右边框x=1,上边框y=1,下边看y=-1的这个坐标系),也就是说x和y的取值范围都是 [-1,1]

                 --- 因为贴图纹理坐标是[0,1],所以这里要把屏幕中的坐标对应那张深度图(屏幕大小的截图)的坐标,要转换坐标,并且Y是相反的 UV(pMVP) *0.5 + 0.5 = [0,1] ,同时纹理y=1-屏幕坐标系的y
                          
范例工程下载:

链接:http://pan.baidu.com/s/1o84C3rk 密码:3cvo

全代码贴出(这里还在CPU上计算了顶点最终的对应的屏幕像素坐标,方便理解):

package
{
	import com.adobe.utils.AGALMiniAssembler;
	import com.adobe.utils.PerspectiveMatrix3D;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.Stage3D;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DRenderMode;
	import flash.display3D.Context3DTextureFormat;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.Texture;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.filters.BlurFilter;
	import flash.filters.GlowFilter;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.ui.Keyboard;
	
	import utils.MatrixUtil;
	
	[SWF(width="1024", height="1024", frameRate="60", backgroundColor="#000000")]
	/**
	 * 锥形并且贴图纹理追加简单光照
	 * @author Xin Yan Kong 2016.4
	 * 
	 */
	public class Main extends Sprite
	{
		/**
		 * 是否首次创建3D环境
		 */		
		private var isFirstCreate:Boolean = true;
		/**
		 * 唯一状态机/画家 
		 */		
		private var context3d:Context3D;
		/**
		 * 模型M矩阵
		 */		
		private var modelMatrix:Matrix3D;
		/**
		 * 镜头V矩阵
		 */		
		private var viewMatrix:Matrix3D;
		/**
		 * 投影P矩阵,Adobe封装的,继承于Matrix3D
		 */		
		private var projectionMatrix:PerspectiveMatrix3D;
		/**
		 * 最终矩阵,这个应该是M+V+P 
		 */		
		private var finalMatrix:Matrix3D;
		/**
		 * flash要用到的顶点信息:顶点缓冲数据(模型) 
		 */		
		private var modelVb:VertexBuffer3D;
		/**
		 * flash要用到的顶点索引信息:索引缓冲数据(模型) 告诉系统点的绘制顺序,系统才知道有多少个面,是怎样的面
		 */		
		private var modelIb:IndexBuffer3D;
		/**
		 * flash要用的顶点信息:顶点缓冲数据(地板)
		 */		
		private var modelVbFloor:VertexBuffer3D;
		/**
		 * flash要用的顶点信息:索引缓冲数据(地板) 
		 */		
		private var modelIbFloor:IndexBuffer3D;
		
		/**
		 * 画家绘制的代码,就是你告诉他应该如何绘制,调色等,他会按照这个方式来绘制,当然也是程序,汇编的风格... 也就是说你可以预先定制好N个代码来选择哪一个来绘制
		 */		
		private var program:Program3D;
		
		private var programShadow:Program3D;
		
		
		private var programHasShadowFloor:Program3D; // 渲染含有阴影的图
		
		/**
		 * 当鼠标移动的时候记录的鼠标点 
		 */		
		private var onMouseDownPt:Point = new Point();
		/**
		 * 嵌入图片资源到SWF里 
		 */		
		[Embed(source = "wall.jpg")]  
		private var wallClass:Class;
		private var wallBmp:Bitmap = new wallClass() as Bitmap;
		/**
		 * 嵌入图片资源到SWF里 
		 */		
		[Embed(source = "ground2.jpg")]  
		private var groundClass:Class;
		private var groundBmp:Bitmap = new groundClass() as Bitmap;
		/**
		 * flash要用到的纹理信息 
		 */		
		private var tex:Texture;
		/**
		 * flash要用到的纹理信息 
		 */		
		private var texGround:Texture;
		/**
		 * 光照所在的点
		 */		
		private var lightPos:Vector3D = new Vector3D(0,0,-15,0);
		/**
		 * 光照强度 
		 */		
		private var lightStr:Number = 0.5;
		private var lightStrAdd:Number = -0.01;
		/**
		 * 深度图储存的texture 
		 */		
		private var texShadow:Texture;
		/**
		 * 测试显示深度图用的BitmapData 
		 */		
		private var bdShadow:BitmapData;
		/**
		 * 测试显示用的深度图Bitmap 
		 */		
		private var shadowMapBmp:Bitmap = new Bitmap();
		/**
		 * 最远的距离 
		 */		
		private var zFar:Number = 50;
		
		
		public function Main()
		{
			// 初始化
			init();
		}
		
		/**
		 * 初始化 
		 * 
		 */		
		private function init():void{
			// -- 如果没有3D环境的话
			if(stage.stage3Ds.length==0){
				throw("你没有可用的3D环境!");
				return;
			}
			// -- 取得一个stage3D舞台,这里取下标0,因为这个3D舞台100%存在。
			var stage3d:Stage3D = stage.stage3Ds[0];
			// -- 设置侦听:创建失败的情况
			stage3d.addEventListener(ErrorEvent.ERROR,onCreate3dError);
			// -- 设置侦听:创建成功的情况或设备恢复的情况
			stage3d.addEventListener(Event.CONTEXT3D_CREATE,onCreate3dSuccess);
			// -- 请求创建,如果你不请求创建,那么又有什么用呢?渲染模式为硬件模式,表示使用显卡GPU来计算
			stage3d.requestContext3D(Context3DRenderMode.AUTO);
		}
		/**
		 * 创建3D环境失败 
		 * @param e
		 * 
		 */		
		private function onCreate3dError(e:ErrorEvent):void{
			throw("创建3D环境失败!");
		}
		/**
		 * 创建3D环境成功的情况或设备恢复的情况 
		 * @param e
		 * 
		 */		
		private var debugTF:TextField = new TextField();
		private function onCreate3dSuccess(e:Event):void{
			// 如果找不到状态机context3d的话提示错误
			context3d = (e.target as Stage3D).context3D;
			if(context3d==null){
				throw("创建3D环境失败!");
				return;
			}
			// 当发生错误的时候会显示错误信息,着色语言阶段出错时开启此选项就会报具体的错误信息,你好因此排查错误(无论首次创建还是设备恢复都要重新设定此项)
			context3d.enableErrorChecking=true;
			// 设定后台缓冲区,一般就是要画画的区域大小了,还有抗锯齿为2的N次方(0表示不抗锯齿画的效率更高但画面更丑)(无论首次创建还是设备恢复都要重新设定此项)
			context3d.configureBackBuffer(stage.stageWidth, stage.stageHeight, 16, true);
			// 如果第一次创建的话而非设备恢复的情况
			if(isFirstCreate){
				// -- 标识下,说明不再是第一次创建3D环境了
				isFirstCreate = false;
				// -- 侦听:每帧渲染
				this.addEventListener(Event.ENTER_FRAME,onRender);
				// -- 侦听:键盘操控 
				stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
				// -- 侦听:鼠标操控
				stage.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
				// -- 初始化物体相关信息
				initModel();
				// -- 初始化shader:着色语言
				initShader();
				// -- 创建镜头V矩阵
				viewMatrix = new Matrix3D();
				// -- 镜头拉后一点,因为屏幕向里是+,所以这个镜头实际在屏幕外的地方
				viewMatrix.appendTranslation(10,0,-10);
				viewMatrix.appendRotation(20,Vector3D.Y_AXIS);
				
//				viewMatrix.appendTranslation(lightPos.x,lightPos.y,lightPos.z);
				
				// -- 创建投影P矩阵
				projectionMatrix = new PerspectiveMatrix3D();
				// -- 设定为透视矩阵,越远越小越接近屏幕中心的那种,右手坐标系RightHand  
				//    -- 第一个参数表示透视角度  
				//    -- 第二个参数表示宽高比例  
				//    -- 第三个参数表示裁剪的最近距离,比这个个还近就无法显示  
				//    -- 第四个参数表示裁剪的最远距离,比这个还远的东西就无法显示  
				projectionMatrix.perspectiveFieldOfViewRH(45.0,stage.stageWidth/stage.stageHeight,0.01,zFar);
				
				// -- 初始化深度图要用到的texture和bitmapData
				texShadow = context3d.createTexture(stage.stageWidth,stage.stageHeight,Context3DTextureFormat.BGRA,true);
				bdShadow = new BitmapData(stage.stageWidth,stage.stageHeight,true,0xffff00);
				this.addChild(shadowMapBmp);
				
				// -- for debug
				debugTF.background = true;
				debugTF.backgroundColor = 0x000000;
				debugTF.mouseEnabled = debugTF.selectable = false;
				debugTF.multiline = true;
				debugTF.textColor = 0xffffff;
				debugTF.alpha = 0.5;
				debugTF.autoSize = TextFieldAutoSize.LEFT;
				this.addChild(debugTF);
				
			}
				// 设备恢复的情况
			else{
				restore();
			}
		}
		/**
		 * 初始化物体相关信息
		 * 
		 */		
		private function initModel():void{
			// -- 最终矩阵
			finalMatrix = new Matrix3D();
			// -- 该物体的矩阵
			modelMatrix = new Matrix3D();
			// -- 该物体的顶点信息(坐标XYZ+对应的颜色RGB)这里5个顶点,我们要绘制一个锥形
			var vecVb:Vector. = Vector.([
				// X  Y Z R G B U V nX nY nZ
				-1, 1,0,1,0,0,0,0,0,0,0, // 0 左上角的点 + 红色 + UV坐标纹理的左上角点 + 法线(后面计算下)
				1, 1,0,0,1,0,1,0,0,0,0, // 1 右上角的点 + 绿色 + UV坐标纹理的右上角点 + 法线(后面计算下)
				1,-1,0,0,0,1,1,1,0,0,0, // 2 右下角的点 + 蓝色 + UV坐标纹理的右下角点 + 法线(后面计算下)
				-1,-1,0,1,1,0,0,1,0,0,0,  // 3 左下角的点 + 黄色 + UV坐标纹理的左下角点 + 法线(后面计算下)
				0,0,-5,0,1,1,0.5,0.5,0,0,0 // 4 里面中心的点 + 青色 + UV坐标纹理的中间的点 + 法线(后面计算下)
			]);
			calcNormal(vecVb,5);
			// -- 创建该物体的顶点缓冲:有多少个顶点(当然是4个),以及一个顶点包含多少个信息(上面一行的信息=11)
			modelVb = context3d.createVertexBuffer(vecVb.length/11,11);
			// -- 通过vector来上传顶点数据:顶点信息、偏移(由于这里vec全部信息只用于一个物体,偏移就是0)、顶点数
			modelVb.uploadFromVector(vecVb,0,vecVb.length/11);
			// -- 该物体的索引数据:按照下面的顺序来画三角形的
			var vecIdx:Vector. = Vector.([
				0,1,2, // 左上角的点 - 右上角的点 - 右下角的点   这里是顺时针绘制的吧,表示一个三角形
				2,3,0, // 右下角的点 - 左下角的点 - 左上角的点  这里也是顺时针绘制的吧,表示第二个三角形
				0,1,4, // 锥形面1
				0,4,3, // 锥形面2
				3,4,2, // 锥形面3
				1,2,4  // 锥形面4
			]);
			// -- 创建该物体的顶点索引缓冲:
			modelIb = context3d.createIndexBuffer(vecIdx.length);
			// -- 通过vector来上传顶点索引数据:顶点索引数据、偏移(同上由于整个vecIdx都用于一个物体了这里全部都是该物体的信息)、索引个数
			modelIb.uploadFromVector(vecIdx,0,vecIdx.length);
			// -- 创建纹理,由于使用BitmapData方式转为texture所以模式是RGBA,如果是ATF格式的话则是COMPRESSED,纹理必须是2的n次方宽高,如果不是可以转化一下
			tex = context3d.createTexture(wallBmp.width,wallBmp.height,Context3DTextureFormat.BGRA,false);
			// -- 上传纹理
			tex.uploadFromBitmapData(wallBmp.bitmapData);
			
			
			// -- 计算木板的顶点和索引
			vecVb = Vector.([
				// X  Y Z R G B U V nX nY nZ
				-10, 10,-20,1,0,0,0,0,0,0,1, // 0 左上角的点 + 红色 + UV坐标纹理的左上角点 + 法线(后面计算下)
				10, 10,-20,0,1,0,30,0,0,0,1, // 1 右上角的点 + 绿色 + UV坐标纹理的右上角点 + 法线(后面计算下)
				10,-10,-20,0,0,1,30,30,0,0,1, // 2 右下角的点 + 蓝色 + UV坐标纹理的右下角点 + 法线(后面计算下)
				-10,-10,-20,1,1,0,0,30,0,0,1,  // 3 左下角的点 + 黄色 + UV坐标纹理的左下角点 + 法线(后面计算下)
				0,0,-5,1,1,0,15,15,0,0,1
				
				
			]);
			modelVbFloor = context3d.createVertexBuffer(vecVb.length/11,11);
			modelVbFloor.uploadFromVector(vecVb,0,vecVb.length/11);
			vecIdx = Vector.([
				0,1,2, // 左上角的点 - 右上角的点 - 右下角的点   这里是顺时针绘制的吧,表示一个三角形
				2,3,0, // 右下角的点 - 左下角的点 - 左上角的点  这里也是顺时针绘制的吧,表示第二个三角形
				0,1,4, // 锥形面1
				0,4,3, // 锥形面2
				3,4,2, // 锥形面3
				1,2,4  // 锥形面4
			]);
			modelIbFloor = context3d.createIndexBuffer(vecIdx.length);
			modelIbFloor.uploadFromVector(vecIdx,0,vecIdx.length);
			// -- 创建纹理,由于使用BitmapData方式转为texture所以模式是RGBA,如果是ATF格式的话则是COMPRESSED,纹理必须是2的n次方宽高,如果不是可以转化一下
		    texGround = context3d.createTexture(groundBmp.width,groundBmp.height,Context3DTextureFormat.BGRA,false);
			// -- 上传纹理
			texGround.uploadFromBitmapData(groundBmp.bitmapData);
		}
		/**
		 * 计算法线 原理就是计算顶点所关联的所有三角形面的法线,然后取得它们平均值就是顶点的法线了(当然还有其他方式求得法线,比如权重)
		 * @param vecVb 顶点信息
		 * @param vertexNum 顶点数
		 * @param offsetNormal 法线的偏移值
		 * @param offsetVertex 顶点的偏移值
		 */		
		private function calcNormal(vecVb:Vector.,vertexNum:int):void{
			var data32PerVertex:int = vecVb.length/vertexNum;
			var vertexArr:Array = [];
			for (var i:int=0;i\n"+ //临时变量 ft0 = v2(UV坐标点)和图片纹理作用的颜色点  关于linear repeat nomip后面再说
				"mov ft1 fc0\n" + // 世界光照点 ft1 = 光照点
				"mov ft2 v3\n" + // 本地法线 ft2 = 法线
				"m44 ft2 ft2 fc3\n" + // 世界法线 ft2 = 法线 m44 模型变换矩阵
				
				//------------------------------------------------------------------------------------------------------
				// 环境反射 fc1
				//------------------------------------------------------------------------------------------------------
				
				//------------------------------------------------------------------------------------------------------
				// 漫反射:
				//   -- 计算光线与法线的角度(点乘)
				//   -- 修正背面负数的情况反转,因为背面的话是负数,角度是负数的话就转为正数(取绝对值或多步骤实现反转)
				//   -- 计算光照加成的颜色 = 像素颜色*角度
				//------------------------------------------------------------------------------------------------------
				// -- 漫反射:计算角度
				"dp3 ft3.w ft1 ft2\n" + // 点乘,计算角度
				// -- 漫反射:修正背面负数的情况反转(取绝对值)
				"abs ft3.w ft3.w\n" + 
				// -- 漫反射:计算光照加成的颜色和根据强度系数调整
				"mul ft4 ft0 ft3.wwww\n" + // 光照加成颜色 ft4 = 原始颜色*光照
				"mul ft4 ft4 fc1.wwww\n" + // 根据强度系数调整
				
				//------------------------------------------------------------------------------------------------------
				// 镜面反射(高光):
				//   -- 计算入射角与光线一半的角度·世界法线
				//   
				//------------------------------------------------------------------------------------------------------
				//				// -- 镜面反射:计算角度  ft2 = 入射角的一半·模型法线
				"dp3 ft6.w fc2 ft2\n" + 
				// -- 镜面反射:修正背面负数的情况反转和计算高光颜色
				"abs ft6.w ft6.w\n" + 
				"mul ft7 fc4 ft6.wwww\n" + 
				
				//------------------------------------------------------------------------------------------------------
				// 计算最终输出的颜色
				//  -- 原始颜色
				//  -- += 漫反射
				//  -- += 高光
				//  -- += 环境光
				//  -- 输出
				//------------------------------------------------------------------------------------------------------
				// -- 镜面反射:高光加成颜色
				"add ft0 ft0 ft4\n" + // 原始颜色 += 漫反射光照加成颜色
				"add ft0 ft0 ft7\n" + // 原始颜色 += 高光加成颜色
				"add ft0 ft0 fc1\n" + // 颜色叠加
				"mov oc, ft0\n" // 直接输出颜色点 ft0  从这里看的出来shader就是用来处理点的颜色信息的,比如颜色混合、光照等等
			);
			program.upload(vShader.agalcode,fShader.agalcode);
			
			// -- 
			initShaderShadow();
			initShaderHasShadowFloor();
		}
		
		/**
		 * 用于输出深度图的shader
		 * 
		 */		
		private function initShaderShadow():void{
			// -- 创建一个着色语言
			programShadow = context3d.createProgram();
			// -- 创建一个AGALMiniAssembler辅助类用于写顶点着色代码
			//    主要用于输出最终的顶点位置,以及传一些数据给片段着色代码使用
			var vShader:AGALMiniAssembler = new AGALMiniAssembler();
			vShader.assemble(Context3DProgramType.VERTEX,
				"m44 vt0 va0 vc0 \n" + // 输出 = 4个顶点 * 最终矩阵 (这里的*就是m44方法,表示每个点都作用了该矩阵)
				"mov v1 vt0\n" +  // MLP z=0-50
				"mov op vt0\n"  // 顶点原始点 
			);
			// -- 创建一个AGALMiniAssembler辅助类用于写片段着色代码
			var fShader:AGALMiniAssembler = new AGALMiniAssembler();
			fShader.assemble(Context3DProgramType.FRAGMENT,
				//------------------------------------------------------------------------------------------------------
				// 通用变量:
				//   -- 纹理颜色
				//   -- 世界光照点 globalLight
				//   -- 世界法线   globalNormal
				//------------------------------------------------------------------------------------------------------
				"mov ft0.xyz v1.zzz\n" + // ft1.x=v3.z ft1.xyz = (顶点·MLP).zzz  将顶点经灯光视角转换的深度存储到xyz
				"mov ft0.w fc0.y\n" + // ft1.w = 1 固定为1
				"div ft0.xyz ft0.xyz fc0.x\n" + // ft0.xyz = ft0.xyz / zFar 因为颜色数值范围在 0-1 所以这里除以zFar来得到0-1 
				"mov oc,ft0" // 输出,这里离摄像机越近越黑(0),越远越白(1)
			);
			programShadow.upload(vShader.agalcode,fShader.agalcode);
			
		}
		/**
		 * 被投影者的代码,
		 * 
		 */	
		private function initShaderHasShadowFloor():void{
			// -- 创建一个着色语言
			programHasShadowFloor = context3d.createProgram();
			// -- 创建一个AGALMiniAssembler辅助类用于写顶点着色代码
			//    主要用于输出最终的顶点位置,以及传一些数据给片段着色代码使用
			var vShader:AGALMiniAssembler = new AGALMiniAssembler();
			vShader.assemble(Context3DProgramType.VERTEX,
				//投影到场景相机
				"m44 vt0 va0 vc0 \n" + // op 顶点****MVP
				//投影到场景灯光相机
				"m44 vt1 va0 vc4\n" + // vt1 = 顶点****MLP
				// 传输MLP
				"mov v0 vt1\n"+ // 投影到场景灯光相机
				// uv
				"mov v1 va2\n"+ // uv
				// 输出
				"mov op vt0\n"
			);
			
			// -- 创建一个AGALMiniAssembler辅助类用于写片段着色代码
			//1.将三维空间坐标转换到设备空间坐标[-1,1]上 (MLP后的坐标.xy/MLP后的坐标.z)
			//2.将设备空间转换到贴图坐标系上 设备空间坐标.xy*0.5+0.5,然后翻转y 这个时候我就知道我当前的点对应的是贴图坐标系中的哪个点了(类似UV坐标)
			//3.根据该计算出的UV坐标取得贴图,能够对应上点
			//4.直接深度对比后展现效果
			var fShader:AGALMiniAssembler = new AGALMiniAssembler();
			fShader.assemble(Context3DProgramType.FRAGMENT,
				// 木板纹理采样
				"tex ft1 v1 fs1<2d,linear,repeat,nomip>\n"+
				// 叠加,Z越大越黑
				"mov ft4.x v0.z\n" + 
				"div ft4.x ft4.x fc2.x\n" + 
				"sub ft1 ft1 ft4.x\n" +
				// 将坐标转换到设备空间坐标(屏幕坐标系) ft2.xy = MLP.xy/MLP.zz
				"div ft2.xy v0.xy v0.zz\n"+
				//将坐标转换到纹理UV fc2.y = 0.5   0.5 * xy + 0.5; 就可以让[-1,1] 转为[0,1]
				//ft2.xy *= 0.5
				"mul ft2.xy ft2.xy fc2.y\n"+
				//ft2.xy += 0.5;
				"add ft2.xy ft2.xy fc2.y\n"+
				//ft2.y = -ft2.y 以下两种写法都可以,因为纹理坐标超出会自动补差,比如1.2就是0.2,比如-0.2 就是0.8
//				"neg ft2.y ft2.y\n"+
				"sub ft2.y fc2.z ft2.y\n" +
				
				//阴影图深度采样
				"tex ft3 ft2.xy fs0<2d,linear,repeat,nomip>\n"+
				// ft5.z = ft3.z * fc2.x  = 深度 * zFar  因为保存的时候除以了zFar,现在还原来做对比
				"mul ft5.z ft3.x fc2.x\n"+
				// ft5.w = ft5.z>=v2.z?1:0 如果当前的深度 大于 (顶点xMLP)的深度的话就返回1  (1或者0)
				"slt ft5.w ft5.z v0.z\n"+ // 影子1,非影子0
				
				// 作出效果,让 影子1,非影子0 转为影子0.5,非影子1,这样直接与原始颜色相乘让影子部分的颜色等于原来的一半
				"mul ft5.w ft5.w fc2.y\n" +
				"neg ft5.w ft5.w\n" +
				"add ft5.w ft5.w fc2.y\n" +
				"add ft5.w ft5.w fc2.y\n" +
				"mul ft1 ft1 ft5.w\n"+
				// oc ft1
				"mov oc ft1\n"
				
			);
			programHasShadowFloor.upload(vShader.agalcode,fShader.agalcode);
		}
		
		/**
		 * 设备恢复的情况 
		 * 
		 */		
		private function restore():void{
			initModel();
			initShader();
		}
		/**
		 * 侦听回调:逐帧渲染 
		 * @param e
		 * 
		 */		
		private function onRender(e:Event):void{
			debugTF.text = "";
			// -- 如果设备丢失的话就暂时不绘制不然就报错了,不信你试试注释掉这个并且CTRL+ALT+DEL
			if(isContextDispose)return;
			// -- 设置深度
			context3d.setDepthTest(true,Context3DCompareMode.LESS);
			// -- 绘制到贴图中
			renderObject();
			// -- 颜色系数改变
			lightStr+=lightStrAdd;
			if(lightStr>1){
				lightStr=1;
				lightStrAdd=-lightStrAdd;
			}
			else if(lightStr<0){
				lightStr=0;
				lightStrAdd=-lightStrAdd;
			}
		}
		
		/**
		 * 将这个图作为tex来试试 
		 * 
		 */		
		
		private function renderObject():void{
			// 以光线角度绘制深度图(GPU模式,无需经过CPU)
			context3d.setRenderToTexture(texShadow,false,0,0);
			context3d.clear(1,1,1);
			lightViewVertebral();
			context3d.setRenderToBackBuffer();
			
			// 以光线角度绘制深度图(GPU绘制后传给CPU使用,如果CPU不使用的话你可以屏蔽掉此段)
			context3d.clear(1,1,1);
			lightViewVertebral();
			context3d.drawToBitmapData(bdShadow);
			texShadow.uploadFromBitmapData(bdShadow);
			shadowMapBmp.bitmapData = bdShadow;
			shadowMapBmp.scaleX=shadowMapBmp.scaleY=0.2;
			shadowMapBmp.x = stage.stageWidth - shadowMapBmp.width;
			
			// 正常渲染场景,附加上影子
			context3d.clear(0,0,1);
			renderFloorTest();
			renderVertebral();
			context3d.present();
		}
		/**
		 * 渲染物体前初始化 
		 * 
		 */		
		private function renderDefault():void{
			context3d.setVertexBufferAt(0, null);
			context3d.setVertexBufferAt(1, null);
			context3d.setVertexBufferAt(2, null);
			context3d.setVertexBufferAt(3, null);
			context3d.setTextureAt(0,null);
			context3d.setTextureAt(1,null);
		}
		/**
		 * 渲染锥形体,以相机视角,带光照和颜色变换效果
		 * 
		 */		
		private function renderVertebral():void{
			// -- 初始化
			renderDefault();
			// -- 最终矩阵先归零
			finalMatrix.identity();
			// -- 乘上M矩阵
			finalMatrix.append(modelMatrix);
			// -- 乘上V矩阵
			finalMatrix.append(viewMatrix);
			// -- 乘上P矩阵
			finalMatrix.append(projectionMatrix);
			// -- 设置状态机当前的VB的坐标 到 va0 ,因为只有XYZ,所以偏移是0,长度是3
			context3d.setVertexBufferAt(0,modelVb,0,Context3DVertexBufferFormat.FLOAT_3);
			// -- 设置状态机当前的VB的颜色 到 va1
			context3d.setVertexBufferAt(1,modelVb,3,Context3DVertexBufferFormat.FLOAT_3);
			// -- 设置状态机当前的VB的UV纹理坐标 到 va2
			context3d.setVertexBufferAt(2,modelVb,6,Context3DVertexBufferFormat.FLOAT_2);
			// -- 设置状态机当前的VB的法线 到 va3
			context3d.setVertexBufferAt(3,modelVb,8,Context3DVertexBufferFormat.FLOAT_3);
			// -- 设置状态机当前的vc0 静态常量:最终矩阵
			context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,finalMatrix,true);
			// -- 设置状态机当前的fc0 静态常量:用于光照计算(这里的光照要进行模型的矩阵变换,因为它直接与法线作用,法线是固定的如果这里不变换的话模型即使变动了受光也是一样的)
			var light:Vector3D=lightPos.clone(); // 不含平移元素的变换
			light.normalize(); // 单位化
			context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,0,Vector.([light.x,light.y,light.z,0]));
			// -- 设置状态机当前的fc1 静态常量:xyz用于 环境光颜色叠加 W用于漫反射光照强度   这里叠加一个红色环境光
			var ambientR:Number = lightStr*0.2;
			var ambientG:Number = (1-lightStr)*0.2;
			var ambientB:Number = (lightStr/2)*0.2;
			context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,1,Vector.([ambientR,ambientG,ambientB,lightStr]));//lightStr
			// -- 设置状态机当前的fc2 静态常量:用于 高光点 这个点是视点和光线点夹角的一半
			var halfEyeLightPos:Vector3D = viewMatrix.position.clone();
			halfEyeLightPos=halfEyeLightPos.add(lightPos);
			//			halfEyeLightPos.scaleBy(0.5); 和下面的xyz/2是一个意思
			halfEyeLightPos.x/=2;
			halfEyeLightPos.y/=2;
			halfEyeLightPos.z/=2;
			halfEyeLightPos.normalize();
			context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,2,Vector.([halfEyeLightPos.x,halfEyeLightPos.y,halfEyeLightPos.z,0]));
			// -- 设置状态机当前的fc3 静态常量:用于将模型矩阵变换传入
			context3d.setProgramConstantsFromMatrix(Context3DProgramType.FRAGMENT,3,modelMatrix);
			// -- 设置状态机当前的fc4 静态常量:高光颜色
			var specularStr:Number = lightStr*0.3;
			context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,4,Vector.([1.0*specularStr,1.0*specularStr,1.0*specularStr,0]));
			// -- 设置状态机使用的纹理到当前的fs0
			context3d.setTextureAt(0,tex);
			// -- 设置状态机使用的代码
			context3d.setProgram(program);
			// -- 绘制三角形
			context3d.drawTriangles(modelIb);
			
		}
		
		/**
		 * 渲染深度图:以光线视角渲染锥形体, 
		 * 
		 */		
		private function lightViewVertebral():void{
			// -- 初始化
			renderDefault();
			// -- 最终矩阵先归零
			finalMatrix.identity();
			// -- 乘上M矩阵
			finalMatrix.append(modelMatrix);
			// -- 乘上V矩阵
			var lightMatrix:Matrix3D = new Matrix3D();
			lightMatrix.appendTranslation(lightPos.x,lightPos.y,lightPos.z);
			finalMatrix.append(lightMatrix.clone());
			// -- 乘上P矩阵
			finalMatrix.append(projectionMatrix);
			// -- 设置状态机当前的VB的坐标 到 va0 ,因为只有XYZ,所以偏移是0,长度是3
			context3d.setVertexBufferAt(0,modelVb,0,Context3DVertexBufferFormat.FLOAT_3);
			// -- 传递最终矩阵(MLP) vc0
			context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,finalMatrix,true);
			// -- 设置状态机当前的fc0 静态常量:x=zFar y=1(为了让渲染图的w固定为1)
			context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,0,Vector.([zFar,1,0,0]));
			// -- 设置状态机使用的代码
			context3d.setProgram(programShadow);
			
			// -- for debug
			var VertebralVec1:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(-1, 1,0));
			var VertebralVec2:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(1, 1,0));
			var VertebralVec3:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(1,-1,0));
			var VertebralVec4:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(-1,-1,0));
			var VertebralVec5:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(0, 0,-5));
			
			VertebralVec1.w = VertebralVec1.z / zFar;
			VertebralVec2.w = VertebralVec2.z / zFar;
			VertebralVec3.w = VertebralVec3.z / zFar;
			VertebralVec4.w = VertebralVec4.z / zFar;
			VertebralVec5.w = VertebralVec5.z / zFar;
			
			// -- 测试顶点·MLP 
			debugTF.appendText("==============锥形体的顶点-绝对3维坐标点"+"\n");
			debugTF.appendText(VertebralVec1.toString()+","+VertebralVec1.w+"\n");
			debugTF.appendText(VertebralVec2.toString()+","+VertebralVec2.w+"\n");
			debugTF.appendText(VertebralVec3.toString()+","+VertebralVec3.w+"\n");
			debugTF.appendText(VertebralVec4.toString()+","+VertebralVec4.w+"\n");
			debugTF.appendText(VertebralVec5.toString()+","+VertebralVec5.w+"\n");
			debugTF.appendText("==============锥形体的顶点-绝对设备2维坐标点"+"\n");
			var VertebralPoint:Point = new Point(VertebralVec1.x/VertebralVec1.z,VertebralVec1.y/VertebralVec1.z); 
			var Vertebra2Point:Point = new Point(VertebralVec2.x/VertebralVec2.z,VertebralVec2.y/VertebralVec2.z); 
			var Vertebra3Point:Point = new Point(VertebralVec3.x/VertebralVec3.z,VertebralVec3.y/VertebralVec3.z); 
			var Vertebra4Point:Point = new Point(VertebralVec4.x/VertebralVec4.z,VertebralVec4.y/VertebralVec4.z); 
			var Vertebra5Point:Point = new Point(VertebralVec5.x/VertebralVec5.z,VertebralVec5.y/VertebralVec5.z); 
			debugTF.appendText(VertebralPoint.toString()+"\n");
			debugTF.appendText(Vertebra2Point.toString()+"\n");
			debugTF.appendText(Vertebra3Point.toString()+"\n");
			debugTF.appendText(Vertebra4Point.toString()+"\n");
			debugTF.appendText(Vertebra5Point.toString()+"\n");
			// -- 转为像素图
			var toTexPointFunc:Function = function(p:Point):String{
				p.x = int((p.x *0.5+0.5)*stage.stageWidth);
				p.y = int((1-(p.y *0.5+0.5))*stage.stageHeight);
				return p.toString() + "\n";
			}
			debugTF.appendText("==============锥形体的顶点-屏幕坐标像素点"+"\n");
			debugTF.appendText(toTexPointFunc(VertebralPoint));
			debugTF.appendText(toTexPointFunc(Vertebra2Point));
			debugTF.appendText(toTexPointFunc(Vertebra3Point));
			debugTF.appendText(toTexPointFunc(Vertebra4Point));
			debugTF.appendText(toTexPointFunc(Vertebra5Point));
			
			// -- 绘制三角形
			context3d.drawTriangles(modelIb);
			
		}
		/**
		 * 渲染被投影的木板,要用到深度图对比,同时
		 * 
		 */		
		var testFloorRo:Number=0;
		var changeRo:Number = 0.5;
		var floorMatrix:Matrix3D = new Matrix3D();
		private function renderFloorTest():void{
			// -- 初始化
			renderDefault();
			
			// -- 最终矩阵先归零
			finalMatrix.identity();
			// -- 乘上M矩阵
			finalMatrix.append(floorMatrix);
			// -- 乘上V矩阵
			finalMatrix.append(viewMatrix);
			// -- 乘上P矩阵
			finalMatrix.append(projectionMatrix);
			// -- 设置状态机当前的vc0 静态常量:最终矩阵
			context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,finalMatrix,true);
			// -- 灯光相机 vc4=MLP
			finalMatrix.identity();
			finalMatrix.append(floorMatrix);
			var lightMatrix:Matrix3D = new Matrix3D();
			lightMatrix.appendTranslation(lightPos.x,lightPos.y,lightPos.z);
			finalMatrix.append(lightMatrix.clone());
			finalMatrix.append(projectionMatrix);
			context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,4,finalMatrix,true);
			
			// -- 设置状态机当前的VB的坐标 到 va0 ,因为只有XYZ,所以偏移是0,长度是3
			context3d.setVertexBufferAt(0,modelVbFloor,0,Context3DVertexBufferFormat.FLOAT_3);
			// -- 设置状态机当前的VB的UV纹理坐标 到 va2
			context3d.setVertexBufferAt(2,modelVbFloor,6,Context3DVertexBufferFormat.FLOAT_2);
			
			// fc2 = x=zFar y=0.5(用于辅助计算)
			context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,2,Vector.([zFar,0.5,1,1]));
			
			// -- 设置状态机使用的纹理到当前的fs0
			context3d.setTextureAt(1,texGround);
			context3d.setTextureAt(0,texShadow);
			// -- 设置状态机使用的代码
			context3d.setProgram(programHasShadowFloor);
			// -- 绘制三角形
			context3d.drawTriangles(modelIbFloor);
			
			// -- for debug
			var VertebralVec1:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(-10, 10,-20));
			var VertebralVec2:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(10, 10,-20));
			var VertebralVec3:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(10,-10,-20));
			var VertebralVec4:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(-10,-10,-20));
			var VertebralVec5:Vector3D = MatrixUtil.transformPoint3D(finalMatrix,new Vector3D(0,0,-5));
			
			VertebralVec1.w = VertebralVec1.z / zFar;
			VertebralVec2.w = VertebralVec2.z / zFar;
			VertebralVec3.w = VertebralVec3.z / zFar;
			VertebralVec4.w = VertebralVec4.z / zFar;
			VertebralVec5.w = VertebralVec5.z / zFar;
			
			// -- 测试顶点·MLP
			debugTF.appendText("==============场地的顶点-绝对3维坐标点"+"\n");
			debugTF.appendText(VertebralVec1.toString()+","+VertebralVec1.w+"\n");
			debugTF.appendText(VertebralVec2.toString()+","+VertebralVec2.w+"\n");
			debugTF.appendText(VertebralVec3.toString()+","+VertebralVec3.w+"\n");
			debugTF.appendText(VertebralVec4.toString()+","+VertebralVec4.w+"\n");
			debugTF.appendText(VertebralVec5.toString()+","+VertebralVec5.w+"\n");
			debugTF.appendText("==============场地的顶点-绝对设备2维坐标点"+"\n");
			var VertebralPoint:Point = new Point(VertebralVec1.x/VertebralVec1.z,VertebralVec1.y/VertebralVec1.z); 
			var Vertebra2Point:Point = new Point(VertebralVec2.x/VertebralVec2.z,VertebralVec2.y/VertebralVec2.z); 
			var Vertebra3Point:Point = new Point(VertebralVec3.x/VertebralVec3.z,VertebralVec3.y/VertebralVec3.z); 
			var Vertebra4Point:Point = new Point(VertebralVec4.x/VertebralVec4.z,VertebralVec4.y/VertebralVec4.z); 
			var Vertebra5Point:Point = new Point(VertebralVec5.x/VertebralVec5.z,VertebralVec5.y/VertebralVec5.z); 
			debugTF.appendText(VertebralPoint.toString()+"\n");
			debugTF.appendText(Vertebra2Point.toString()+"\n");
			debugTF.appendText(Vertebra3Point.toString()+"\n");
			debugTF.appendText(Vertebra4Point.toString()+"\n");
			debugTF.appendText(Vertebra5Point.toString()+"\n");
			// -- 转为像素图
			var toTexPointFunc:Function = function(p:Point):String{
				p.x = int((p.x *0.5+0.5)*stage.stageWidth);
				p.y = int((1-(p.y *0.5+0.5))*stage.stageHeight);
				return p.toString() + "\n";
			}
			debugTF.appendText("==============场地的顶点-屏幕坐标像素点"+"\n");
			debugTF.appendText(toTexPointFunc(VertebralPoint));
			debugTF.appendText(toTexPointFunc(Vertebra2Point));
			debugTF.appendText(toTexPointFunc(Vertebra3Point));
			debugTF.appendText(toTexPointFunc(Vertebra4Point));
			debugTF.appendText(toTexPointFunc(Vertebra5Point));
			debugTF.appendText("==============按键:"+"\n");
			debugTF.appendText("WASD=锥形体位移"+"\n");
			debugTF.appendText("IJKL=场地位移"+"\n");
			debugTF.appendText("方向键=镜头平移"+"\n");
			debugTF.appendText("小键盘789=镜头旋转"+"\n");
			debugTF.appendText("鼠标拖拽=锥形体旋转"+"\n");
		}
		
		/**
		 * 侦听回调:键盘操作 
		 * @param e
		 * 
		 */		
		private function onKeyDown(e:KeyboardEvent):void{
			switch(e.keyCode){
				case Keyboard.W: 
					modelMatrix.appendTranslation(0,0,0.1);
					break;
				case Keyboard.S: 
					modelMatrix.appendTranslation(0,0,-0.1);
					break;
				case Keyboard.A: 
					modelMatrix.appendTranslation(0.1,0,0);
					break;
				case Keyboard.D:
					modelMatrix.appendTranslation(-0.1,0,0);
					break;
				case Keyboard.I: 
					floorMatrix.appendTranslation(0,0,0.1);
					break;
				case Keyboard.K: 
					floorMatrix.appendTranslation(0,0,-0.1);
					break;
				case Keyboard.J:
					floorMatrix.appendTranslation(0.1,0,0);
					break;
				case Keyboard.L:
					floorMatrix.appendTranslation(-0.1,0,0);
					break;
				case Keyboard.UP: 
					viewMatrix.appendTranslation(0,0,-0.1);
					break;
				case Keyboard.DOWN: 
					viewMatrix.appendTranslation(0,0,0.1);
					break;
				case Keyboard.LEFT: 
					viewMatrix.appendTranslation(-0.1,0,0);
					break;
				case Keyboard.RIGHT:
					viewMatrix.appendTranslation(0.1,0,0);
					break;
				case Keyboard.NUMPAD_7:
					viewMatrix.appendRotation(1,Vector3D.X_AXIS);
					break;
				case Keyboard.NUMPAD_8:
					viewMatrix.appendRotation(1,Vector3D.Y_AXIS);
					break;
				case Keyboard.NUMPAD_9:
					viewMatrix.appendRotation(1,Vector3D.Z_AXIS);
					break; 
			}
		}
		/**
		 * 鼠标移动旋转物体
		 * 原理无非就是根据每次移动时的像素差距来计算让物体矩阵M在当前的状态下再围绕X和Y旋转(至于围绕Z轴旋转可以自己添加试试)
		 * @param e
		 * 
		 */		
		private function onMouseDown(e:MouseEvent):void{
			onMouseDownPt.x = e.stageX;
			onMouseDownPt.y = e.stageY;
			stage.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
			stage.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
		}
		private function onMouseMove(e:MouseEvent):void{
			var dx:Number = e.stageX - onMouseDownPt.x;
			var dy:Number = e.stageY - onMouseDownPt.y;
			var degreesY:Number = dx/2;
			var degreesX:Number = dy/2;
			onMouseDownPt.x = e.stageX;
			onMouseDownPt.y = e.stageY;
			modelMatrix.appendRotation(degreesY,Vector3D.Y_AXIS);
			modelMatrix.appendRotation(degreesX,Vector3D.X_AXIS);
		}
		private function onMouseUp(e:MouseEvent):void{
			stage.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseUp);
		}
		/**
		 * 判断设备丢失 
		 * 
		 */		
		private function get isContextDispose():Boolean{
			return context3d==null||context3d.driverInfo=="Disposed"||context3d.driverInfo=="";
		}
		
		
	}
}





你可能感兴趣的:(技术-语言-AS3,技术-3D图形开发,技术-3D引擎开发)