在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 正方体网格

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
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.<Number>;
        private var m_cubeIndex : Vector.<uint>;
       
        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.<Number>();
            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.<uint>();
            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);
        }
    }
}

文档类
CubeCamera.as

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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
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.<CubeMesh> = new Vector.<CubeMesh>();
        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;
    }
}

发表评论

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.