在stage3d中实现相机跟随效果

话说,这个月的游戏坑掉了,都是因为看了好多书的说
3D数学基础:图形与游戏开发,DirectX_9.0_3D游戏开发编程基础,C++ Primer
争取下个月给力些吧~

进入整体,在stage3D中实现一个相机. 3D的相机如果不做复杂的室内相机(避免穿越到墙壁外部)的话. 真是比2D相机好简单多呢.
在3D中,相机就是一个向量. 因此我们需要2个值, 相机的位置 以及 相机的朝向.

在stage3D中,表示一个向量或是坐标,都可以使用Matrix3D这个类.我们在创建相机的时候,通过appendTranslate来设置位置
var camera : Matrix3D= new Matrix3D();
camera.appendTranslate(0,0,0);
之后,我们通过设置相机的朝向,来确定相机这个向量.
camera.pointAt(目标位置(Vector3D类型),世界坐标系中的前方(Vector3D类型),世界坐标系中的上方(Vector3D类型);

stage3D中,一般约定使用右手坐标系,因此 前方 就是 Z轴为负的向量. 上方 无论右手还是左手坐标系 都是 Y轴为正的向量.
那么如果我们要看一个位置为 0,0,-10 的方向.可以这么设置
camera.pointAt(new Vector3D(0,0,-10), new Vector3D(0,0,1), new Vector3D(0,1,0));

通过上面几个步骤,3D摄像机就基本设置完毕了. 但是有一个很重要的地方. 在3D世界中,假设你让一个物体往左边移动,本质并不是物体往左边移动,而是整个世界往右边移动了.
作为相机,必须与世界反向.
所以之前所有的属性都必须为反向,这样,当我们反转相机之后,值才正确.
那么相机的pointAt中的 朝向和上方都要写反值,坐标也要取反
camera.pointAt(new Vector3D(0,0,-10), new Vector3D(0,0,-1), new Vector3D(0,-1,0));

最后在渲染矩阵的时候,然如下顺序添加个矩阵,即可
finalMatrix.append(模型);
finalMatrix.append(相机);
finalMatrix.append(世界);
finalMatrix.append(屏幕投影);

下面是一个相机的Demo : http://www.dreamfairy.cn/blog/work/flash/3d/stage3dCamera/CubeCamera.html
W,S,A,D来控制相机瞄准的飞船移动
方向键来控制相机的移动

接下来是源码:

CubeMesh.as 正方体网格
[cc lang=”actionscript3″ nowra=”false”]
package C3
{
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;

public class CubeMesh
{
private var m_matrix : Matrix3D = new Matrix3D();
private var m_transform : Matrix3D = new Matrix3D();

private var m_context3D : Context3D;
private var m_vertexBuffer : VertexBuffer3D;
private var m_indexBuffer : IndexBuffer3D;
private var m_cubeVertex : Vector.;
private var m_cubeIndex : Vector.;

private var m_pos : Vector3D = new Vector3D();
private var m_rotate : Vector3D = new Vector3D();

private var m_needUpdateTransform : Boolean = false;

public function CubeMesh(context3D : Context3D)
{
m_context3D = context3D;

setup();
}

public function render(view : Matrix3D, proj : Matrix3D, shader : Program3D) : void
{
updateTransform();

m_matrix.identity();
m_matrix.append(m_transform);
m_matrix.append(view);
m_matrix.append(proj);

m_context3D.setProgram(shader);
m_context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m_matrix, true);

m_context3D.setVertexBufferAt(0,m_vertexBuffer,0,Context3DVertexBufferFormat.FLOAT_3);
m_context3D.drawTriangles(m_indexBuffer,0,m_cubeIndex.length / 3);

m_context3D.setVertexBufferAt(0,null);
m_context3D.setVertexBufferAt(1,null);
}

public function updateTransform() : void
{
if(!m_needUpdateTransform) return;

m_transform.identity();
m_transform.appendRotation(m_rotate.x, Vector3D.X_AXIS);
m_transform.appendRotation(m_rotate.y, Vector3D.Y_AXIS);
m_transform.appendRotation(m_rotate.z, Vector3D.Z_AXIS);

m_transform.appendTranslation(m_pos.x,m_pos.y,m_pos.z);
}

public function moveTo(x : Number, y : Number, z : Number) : void
{
m_pos.x = x;
m_pos.y = y;
m_pos.z = z;

m_needUpdateTransform = true;
}

public function rotation(value : Number, axis : Vector3D) : void
{
switch(axis){
case Vector3D.X_AXIS:
m_rotate.x = value;
break;
case Vector3D.Y_AXIS:
m_rotate.y = value;
break;
case Vector3D.Z_AXIS:
m_rotate.z = value;
break;
}

m_needUpdateTransform = true;
}

public function move(x : Number, y : Number, z : Number) : void
{
m_pos.x += x;
m_pos.y += y;
m_pos.z += z;

m_needUpdateTransform = true;
}

public function get position() : Vector3D
{
return m_transform.position;
}

private function setup() : void
{
m_cubeVertex = new Vector.();
m_cubeVertex.push(-1,-1,-1);
m_cubeVertex.push(-1,1,-1);
m_cubeVertex.push(1,1,-1);
m_cubeVertex.push(1,-1,-1);
m_cubeVertex.push(-1,-1,1);
m_cubeVertex.push(-1,1,1);
m_cubeVertex.push(1,1,1);
m_cubeVertex.push(1,-1,1);

m_cubeIndex = new Vector.();
m_cubeIndex.push(0,1,2);
m_cubeIndex.push(0,2,3);
m_cubeIndex.push(4,6,5);
m_cubeIndex.push(4,7,6);
m_cubeIndex.push(4,5,1);
m_cubeIndex.push(4,1,0);
m_cubeIndex.push(3,2,6);
m_cubeIndex.push(3,6,7);
m_cubeIndex.push(1,5,6);
m_cubeIndex.push(1,6,2);
m_cubeIndex.push(4,0,3);
m_cubeIndex.push(4,3,7);

m_vertexBuffer = m_context3D.createVertexBuffer(m_cubeVertex.length / 3, 3);
m_vertexBuffer.uploadFromVector(m_cubeVertex,0,m_cubeVertex.length / 3);

m_indexBuffer = m_context3D.createIndexBuffer(m_cubeIndex.length);
m_indexBuffer.uploadFromVector(m_cubeIndex,0,m_cubeIndex.length);
}
}
}
[/cc]

文档类
CubeCamera.as
[cc lang=”actionscript3″ nowrap=”false”]
package C3
{
import com.adobe.utils.AGALMiniAssembler;
import com.adobe.utils.PerspectiveMatrix3D;

import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Program3D;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.text.TextField;
import flash.ui.Keyboard;

[SWF(width=”640″,height=”480″,frameRate=”60″)]
public class CubeCamera extends Sprite
{
public function CubeCamera()
{
stage ? init() : addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null) : void
{
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
stage.stage3Ds[0].requestContext3D();

var tf : TextField = new TextField();
tf.textColor = 0xffffff;
tf.width = 300;
tf.text = “W,S,A,D 控制飞船移动n方向键控制相机移动”;
addChild(tf);
}

private function onContextCreate(e:Event) : void
{
if(hasEventListener(Event.ENTER_FRAME))
removeEventListener(Event.ENTER_FRAME, onEnter);

m_context = (e.target as Stage3D).context3D;

m_context.configureBackBuffer(stage.stageWidth,stage.stageHeight,2,true);
m_context.enableErrorChecking = true;

setup();

addEventListener(Event.ENTER_FRAME, onEnter);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}

private function onKeyUp(e:KeyboardEvent) : void
{
delete m_key[e.keyCode];
}

private function onKeyDown(e:KeyboardEvent) : void
{
m_key[e.keyCode] = true;
}

private function setup() : void
{
var count : uint = 30;

for(var i : int = 0; i < count ; i++) { var cubeMesh : CubeMesh = new CubeMesh(m_context); cubeMesh.moveTo(Math.random() * -count, Math.random() * -count, Math.random() * -count); m_cubeList.push(cubeMesh); } m_teapotMesh = new TeapotMesh(m_context); m_teapotMesh.moveTo(0,0,0); m_teapotMesh.rotation(-90,Vector3D.Z_AXIS); // m_teapotMesh.rotation(90, Vector3D.X_AXIS); m_shader = m_context.createProgram(); var vertexShader : AGALMiniAssembler = new AGALMiniAssembler(); vertexShader.assemble(Context3DProgramType.VERTEX, [ "m44 op, va0, vc0", "mov v0, va0", "mov v1, va1", ].join("n")); var fragmentShader : AGALMiniAssembler = new AGALMiniAssembler(); fragmentShader.assemble(Context3DProgramType.FRAGMENT, [ "tex ft0, v1, fc0<2d,wrap,linear>“,
“mov oc, ft0”,
].join(“n”));

m_shader.upload(vertexShader.agalcode, fragmentShader.agalcode);

m_shaderNoTexture = m_context.createProgram();
vertexShader = new AGALMiniAssembler();
vertexShader.assemble(Context3DProgramType.VERTEX,
[
“m44 op, va0, vc0”,
“mov v0, va0”,
].join(“n”));

fragmentShader = new AGALMiniAssembler();
fragmentShader.assemble(Context3DProgramType.FRAGMENT,
[
“mov oc, v0”,
].join(“n”));

m_shaderNoTexture.upload(vertexShader.agalcode, fragmentShader.agalcode);

m_proj.identity();
m_proj.perspectiveFieldOfViewRH(45,stage.stageWidth / stage.stageHeight, 0.001, 1000);

m_viewMatrix.appendTranslation(0,0,10);
}

private function onEnter(e:Event) : void
{
m_context.clear();
renderScene();
renderKeyBoard();
m_context.present();
}

private var n : Number = 0;
private function renderScene() : void
{
n += .01;

//相机上下浮动
// m_viewMatrix.appendTranslation(0,Math.sin(y) / 10,0);
m_viewMatrix.pointAt(m_teapotMesh.position, CAM_FACING, CAM_UP);
m_camera = m_viewMatrix.clone();
m_camera.invert();

//矩形做自转运动
m_cubeList[0].rotation(n * 33.3,Vector3D.Y_AXIS);

for each(var cubeMesh : CubeMesh in m_cubeList)
{
cubeMesh.render(m_camera,m_proj,m_shaderNoTexture);
}

m_teapotMesh.render(m_camera,m_proj,m_shader);
}

private function renderKeyBoard() : void
{
var speed : Number = .5;

if(m_key[37])
m_viewMatrix.appendTranslation(-speed,0,0);

if(m_key[38])
m_viewMatrix.appendTranslation(0,0,-speed);

if(m_key[39])
m_viewMatrix.appendTranslation(speed,0,0);

if(m_key[40])
m_viewMatrix.appendTranslation(0,0,speed);

if(m_key[Keyboard.A]){
m_teapotMesh.move(-speed,0,0);
}

if(m_key[Keyboard.D]){
m_teapotMesh.move(speed,0,0);
}

if(m_key[Keyboard.W]){
m_teapotMesh.move(0,speed,0);
}

if(m_key[Keyboard.S]){
m_teapotMesh.move(0,-speed,0);
}
}

private static const CAM_FACING:Vector3D = new Vector3D(0, 0, -1);
private static const CAM_UP:Vector3D = new Vector3D(0, -1, 0);

private var m_teapotMesh : TeapotMesh;
private var m_cubeList : Vector. = new Vector.();
private var m_context : Context3D;
private var m_shader : Program3D;
private var m_shaderNoTexture : Program3D;
private var m_key : Object = new Object();

private var m_proj : PerspectiveMatrix3D = new PerspectiveMatrix3D();
private var m_viewMatrix : Matrix3D = new Matrix3D();
private var m_camera : Matrix3D;
}
}
[/cc]

发表评论

电子邮件地址不会被公开。 必填项已用*标注