Stage3D_Game_Programming:渲染3D模型
OBJ是文件,先来解释下OBJ文件。随便找一个OBJ文件,用文本查看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
# Max2Obj Version 4.0 Mar 10th, 2001 # # object ( null ) to come ... # v - 0.257 0.191 0.423 v - 0.115 0.29 - 0.068 v - 0.237 0 - 0.074 v - 0.237 0.071 0.646 ... # 82 vertices vt 0.623 0.227 0 vt 0.615 0.53 0 vt 0.895 0.555 0 vt 0.825 0.246 0 vt 0.647 0.847 0 ... # 39 texture vertices f 1 / 1 2 / 2 3 / 3 f 3 / 3 4 / 4 1 / 1 f 2 / 2 5 / 5 6 / 6 f 6 / 6 3 / 3 2 / 2 ... # 142 faces |
v:是模型的顶点信息 vt:是贴图的UV坐标 f:是定义面的顶点索引和顶点对应的UV坐标索引 清楚了OBJ文件里各种数据所代表的信息,接下来就是如何把数据传递给显卡渲染。 那么我们需要一个解析OBJ文件的类(代码比较简单,注释就省了)
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
|
package com.parser { import flash.utils.ByteArray; public class OBJParser { private var _lines: Array ; private const LINE_FEED: String = String .fromCharCode( 10 ); private const SPACE: String = String .fromCharCode( 32 ); private var _scale: Number ; public function OBJParser(objfile:ByteArray,scale: Number = 1 ) { _scale = scale; if (objfile) { var lineStr: String = parserToStr(objfile); _lines = lineStr.split(LINE_FEED); var loop: uint = _lines.length; for ( var i: uint = 0 ; i < loop; i++) { parseLine(_lines[i]); } } } private const VERTEX: String = 'v' ; private const UV: String = 'vt' ; private const INDEX_DATA: String = 'f' ; private function parseLine(lineStr: String ): void { var data: Array = lineStr.split(SPACE); if (!data.length) return ; var key: String = data[ 0 ]; var parseData: Array = data.slice( 1 ); switch (key) { case VERTEX: parseVertex(parseData); break ; case UV: parseUV(parseData); break ; case INDEX_DATA: parseIndexData(parseData); break ; } } private var _vertices:Vector.< Number > = new Vector.< Number >(); private function parseVertex(data: Array ): void { if (data[ 0 ] == '' || data[ 0 ] == " " ) { data = data.slice( 1 ); } var loop: uint = data.length; for ( var i: uint = 0 ; i < loop; i++) { var value: Number = Number (data[i]); _vertices.push(value*_scale); } } private var _uvs:Vector.< Number > = new Vector.< Number >(); private function parseUV(data: Array ): void { if (data[ 0 ] == '' || data[ 0 ] == " " ) { data = data.slice( 1 ); } var loop: uint = 2 ; for ( var i: uint = 0 ; i < loop; i++) { var value: Number = Number (data[i]); _uvs.push(value*_scale); } } private const SLASH: String = "/" ; private var _indexData:Vector.< uint > = new Vector.< uint >(); private var _vertexsData:Vector.< Number > = new Vector.< Number >(); private var _uvData:Vector.< Number > = new Vector.< Number >(); private var _faceIndex: uint ; private function parseIndexData(data: Array ): void { var index: uint = 0 ; while ((data[index] == '' ) || (data[index] == ' ' ))index++; var loop: uint = index+ 3 ; var vertexIndex: int ; var uvIndex: int ; var normalIndex: int ; for ( var i: uint = index; i < loop; i++) { var triplet: String = data[i]; var subdata: Array = triplet.split(SLASH); vertexIndex = int (subdata[ 0 ]) - 1 ; uvIndex = int (subdata[ 1 ]) - 1 ; if (vertexIndex < 0 ) vertexIndex = 0 ; if (uvIndex < 0 ) uvIndex = 0 ; index = 3 *vertexIndex; _vertexsData.push(_vertices[index + 0 ], _vertices[index + 1 ], _vertices[index + 2 ]); index = 2 *uvIndex; _uvData.push( 1 -_uvs[index+ 0 ], 1 -_uvs[index+ 1 ]); } _indexData.push(_faceIndex+ 0 ,_faceIndex+ 1 ,_faceIndex+ 2 ); _faceIndex += 3 ; } private function parserToStr(objFileByteArray:ByteArray): String { return objFileByteArray.readUTFBytes(objFileByteArray.bytesAvailable); } /** * 顶点数据 * @return * */ public function get vertexsData():Vector.< Number > { return _vertexsData; } /** * UV数据 * @return * */ public function get uvData():Vector.< Number > { return _uvData; } /** * 索引数据 * @return * */ public function get indexData():Vector.< uint > { return _indexData; } } } |
1
|
|
有了顶点信息,uv信息和面的索引信息接下来就可以把这货渲染出来了。
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
package { import com.adobe.utils.AGALMiniAssembler; import com.adobe.utils.PerspectiveMatrix3D; import com.parser.OBJParser; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.display.Stage3D; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.display3D.Context3D; import flash.display3D.Context3DProgramType; 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.Event; import flash.geom.Matrix; import flash.geom.Matrix3D; import flash.geom.Vector3D; import flash.utils.ByteArray; [SWF(width= '800' ,height= '600' ,backgroundColor= '0x333333' ,frameRate= "60" )] public class GameTest extends Sprite { [Embed (source = "art/spaceship.obj" , mimeType = "application/octet-stream" )] private var objData:Class; [Embed (source = "art/spaceship_texture.jpg" )] private var TextureBitmap:Class; private var textureData:Bitmap = new TextureBitmap(); private var _stage3D:Stage3D; private var _context3D:Context3D; private const sw: uint = 700 ; private const sh: uint = 500 ; private var _objParser:OBJParser; //顶点缓冲 存储顶点信息 private var _vertexBuffer:VertexBuffer3D; //顶点缓冲 存储UV信息 private var _uvBuffer:VertexBuffer3D; //顶点索引 private var _indexBuffer:IndexBuffer3D; private var _texture:Texture; private var _textureSize: uint = 512 ; private var _projectionmatrix:PerspectiveMatrix3D; private var _viewmatrix:Matrix3D; private var _modelmatrix:Matrix3D = new Matrix3D(); private var _modelViewProjection:Matrix3D = new Matrix3D(); private var _vertexShaderAssembler:AGALMiniAssembler; private var _fragmentAssembler:AGALMiniAssembler; private var _program:Program3D; public function GameTest() { if ( this .stage) { init(); } else { addEventListener(Event.ADDED_TO_STAGE,init); } } private function init(e:Event = null ): void { if (hasEventListener(Event.ADDED_TO_STAGE))removeEventListener(Event.ADDED_TO_STAGE,init); this .stage.scaleMode = StageScaleMode.NO_SCALE; this .stage.align = StageAlign.TOP_LEFT; initStage3D(); } private function initStage3D(): void { _stage3D = this .stage.stage3Ds[ 0 ]; if (_stage3D) { _stage3D.addEventListener(Event.CONTEXT3D_CREATE,onContext3DCreate); _stage3D.requestContext3D(); } } private function onContext3DCreate(e:Event): void { _context3D = _stage3D.context3D; if (_context3D == null ) { throw new Error( "无法创建Context3D" ); return ; } _stage3D.x = ( this .stage.stageWidth - sw)/ 2 ; _stage3D.y = ( this .stage.stageHeight - sh)/ 2 ; _context3D.configureBackBuffer(sw,sh, 1 ); _context3D.clear( 205 , 205 , 205 ); _context3D.enableErrorChecking = true ; initData(); initShader(); this .stage.addEventListener(Event.ENTER_FRAME,onEnterFrame); } private function initData(): void { var objdata:ByteArray = new objData() as ByteArray; _objParser = new OBJParser(objdata); var vertexCont: uint = _objParser.vertexsData.length/ 3 ; _vertexBuffer = _context3D.createVertexBuffer(vertexCont, 3 ); _vertexBuffer.uploadFromVector(_objParser.vertexsData, 0 ,vertexCont); var uvCont: uint = _objParser.uvData.length/ 2 ; _uvBuffer = _context3D.createVertexBuffer(uvCont, 2 ); _uvBuffer.uploadFromVector(_objParser.uvData, 0 ,uvCont); _context3D.setVertexBufferAt( 0 ,_vertexBuffer, 0 ,Context3DVertexBufferFormat.FLOAT_3); _context3D.setVertexBufferAt( 1 ,_uvBuffer, 0 ,Context3DVertexBufferFormat.FLOAT_2); var indexData:Vector.< uint > = _objParser.indexData; _indexBuffer = _context3D.createIndexBuffer(indexData.length); _indexBuffer.uploadFromVector(indexData, 0 ,indexData.length); _texture = _context3D.createTexture(_textureSize,_textureSize,Context3DTextureFormat.BGRA, false ); uploadTextureWithMipmaps(_texture,textureData.bitmapData); _projectionmatrix = new PerspectiveMatrix3D(); _projectionmatrix.identity(); // 45 degrees FOV, 640/480 aspect ratio, 0.1=near, 100=far _projectionmatrix.perspectiveFieldOfViewRH( 45.0 , sw / sh, 0.01 , 100.0 ); _viewmatrix = new Matrix3D(); // camera Matrix3D _viewmatrix.identity(); // 移动镜头到(0,0,0) _viewmatrix.appendTranslation( 0 , 0 , 0 ); } private function initShader(): void { _vertexShaderAssembler = new AGALMiniAssembler(); _vertexShaderAssembler.assemble(Context3DProgramType.VERTEX, "m44 op, va0, vc0\n" + "mov v0, va0\n" + "mov v1, va1\n" ); _fragmentAssembler= new AGALMiniAssembler(); _fragmentAssembler..assemble ( Context3DProgramType.FRAGMENT, "tex ft0, v1, fs0 <2d,linear,repeat,miplinear>\n" + "mov oc, ft0\n" ); _program = _context3D.createProgram(); _program.upload(_vertexShaderAssembler.agalcode,_fragmentAssembler.agalcode); _context3D.setTextureAt( 0 ,_texture); _context3D.setProgram(_program); } private var _t: Number = 0 ; private function onEnterFrame(e:Event): void { _context3D.clear( 0 , 0 , 0 ,. 7 ); _t += 2.0 ; _modelmatrix.identity(); //旋转模型 _modelmatrix.appendRotation(_t* 1.0 , Vector3D.Y_AXIS); _modelmatrix.appendRotation(_t*- 0.2 , Vector3D.X_AXIS); _modelmatrix.appendRotation(_t* 0.3 , Vector3D.Y_AXIS); _modelmatrix.appendTranslation(- 0.4 , 0 , - 5 ); _modelViewProjection.identity(); _modelViewProjection.append(_modelmatrix); _modelViewProjection.append(_viewmatrix); _modelViewProjection.append(_projectionmatrix); _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0 , _modelViewProjection, true ); //绘制模型 _context3D.drawTriangles(_indexBuffer, 0 , _objParser.indexData.length/ 3 ); //呈现至屏幕 _context3D.present(); } /** * Mipmap * @param dest * @param src * */ private function uploadTextureWithMipmaps(dest:Texture, src:BitmapData): void { var ws: int = src.width; var hs: int = src.height; var level: int = 0 ; var tmp:BitmapData; var transform:Matrix = new Matrix(); tmp = new BitmapData(src.width, src.height, true , 0 ); while ( ws >= 1 && hs >= 1 ) { tmp.draw(src, transform, null , null , null , true ); dest.uploadFromBitmapData(tmp, level); transform.scale( 0.5 , 0.5 ); level++; ws >>= 1 ; hs >>= 1 ; if (hs && ws) { tmp.dispose(); tmp = new BitmapData(ws, hs, true , 0x00000000 ); } } tmp.dispose(); } } } |