Graphics Programming

[Marco Scabia] 04 안녕 삼각형 본문

Season 1/플래시

[Marco Scabia] 04 안녕 삼각형

minseoklee 2011. 11. 8. 18:50
다음 글을 번역한 것입니다.
http://www.adobe.com/devnet/flashplayer/articles/hello-triangle.html



Prerequisite knowledge

Familiarity working with the Stage3D API and a basic understanding of how it is structured is required. Basic knowledge of Vertex and Fragment Shaders, and the AGAL shading language is also recommended. Before completing this tutorial, be sure to follow along with the previous tutorials in this series on Stage3D (1. How Stage3D works, 2. Vertex and Fragment Shaders, 3. What is AGAL).

 

Additional required other products (third-party/labs/open source)

Flex 4.5 SDK

 

Flash Player 11 version of the playerglobal.swc file

 

Flash Player 11

User level

Intermediate

Required products

Sample files





























이 글에서는 Stage3D API를 토대로 실제로 작동하는 액션스크립트 애플리케이션을 시험해봅니다. 먼저 Stage3D가 준비된 빌드 환경을 알맞게 구성하는 방법을 배웁니다. 견본 프로젝트를 갖추고 나면 액션스크립트에서 Stage3D를 초기화하는 방법과 Stage3D를 이용해서 단색 삼각형을 포함하는 아주 간단한 3D 장면을 만들어 그리는 방법을 살펴봅니다.

마지막으로 텍스처 맵핑을 적용하는 과정을 들여다보며 텍스처 맵핑된 기하 구조를 가지고 Stage3D 애플리케이션을 재검토합니다.

빌드 환경 갖추기

Stage3D 앱 제작을 시작할 때 가장 먼저 할 일은 빌드 환경을 준비하고 알맞게 구성되었는지 확인하는 일입니다.

명령줄에서 빈약한 플렉스 SDK만 가지고 Stage3D 애플리케이션을 제작하는 것도 확실히 가능하지만 대부분은 플래시 빌더 4.5 같은 통합 도구를 쓰는 것이 보다 편리합니다. 이 글에서 설명하는 과정은 플래시 빌더 4.5로 작업하는 것에 초점을 뒀습니다.

최신 플렉스 4.5 SDK(4.5.0.20967 이상의 버전)를 받아 설치하는 것으로 시작합니다. 플렉스 4.5 SDK를 내려받는 링크는 위의 Requirements 부분에 있습니다.

다음으로 플래시 플레이어 11 버전의 playerglobal.swc 파일을 내려받아 플렉스 4.5 SDK에 설치합니다. SWC 파일을 복사하고 플렉스 SDK 폴더의 다음 위치에 붙여넣으면 됩니다.

<플렉스 SDK 루트 폴더>\frameworks\libs\player\11.0

주의 : 여러분이 11.0 폴더를 직접 만들어서 SWC 파일을 넣어야 합니다. 필요하다면 파일 이름을 playerglobal.swc로 바꾸세요. SDK의 다음 버전에서는 플래시 플레이어 11 버전의 playerglobal.swc가 기본으로 포함될 것이니 이 단계가 더이상 필요하지 않을 것입니다.

그 다음 플렉스 4.5 SDK를 플래시 빌더 4.5 환경에 추가합니다. 그러려면 Preferences > Flash Builder > Installed Flex SDKs를 선택하여 새로운 플렉스 4.5 SDK를 추가합니다.

물론 Stage3D를 지원하는 플래시 플레이어 버전 11도 받아서 설치해야 합니다! 아직 안 했다면 Requirements에 있는 링크를 타고 내려받아 설치합니다.

이제 플렉스 4.5 SDK를 playerglobal.swc 파일과 함께 준비했고 플렉스 빌더에도 추가했으니 새 액션스크립트 프로젝트를 만들 준비가 끝났습니다.

그에 앞서 여러분의 프로젝트가 SWF 버전 13을 가리키도록 구성해야 합니다. Project Properties를 열고 ActionScript Compiler 탭을 누릅니다. "Additional compiler arguments"에 "-swf-version=13"을 그대로 입력합니다(쌍따옴표는 빼고). 같은 패널에서 앱이 플래시 플레이어 버전 11을 가리키는지도 확인합니다.

이제 마지막으로 할 일은 플래시 플레이어가 3D 하드웨어 가속을 사용하도록 하기 위해 WMODE를 "direct"로 지정하는 것입니다. 플래시 빌더에서 index.template.html 파일을 열고 params가 SWF 파일로 넘겨지는 부분을 찾아 다음 줄을 추가합니다.

params.wmode = "direct";

그림 1의 스크린샷에 이 줄을 추가할 부분이 나옵니다.
Figure 1. Setting wmode = "direct" in the index.template.html file.그림 1. index.template.html 파일에서 wmode = "direct"로 지정하기.

이 표시 설정은 플래시 플레이어를 목표로 하는 경우입니다. AIR 애플리케이션을 제작할 때는 app descriptor에서 renderMode 요소를 direct로 지정합니다.

프로젝트를 빌드해서 잘 작동하는지 확인하십시오. 그저 빈 윈도우가 보여야 합니다.

Stage3D 초기화하기

액션스크립트 앱을 갖췄으면 Stage3D를 초기화하는 것이 가장 먼저 할 일입니다.

3D 렌더링을 수행하려면 3D 렌더링 표면을 제공하는, Context3D 클래스의 인스턴스가 필요합니다.

생성자에서 다음 줄을 추가합니다.

public function HelloTriangleColored()
{
stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initMolehill );
stage.stage3Ds[0].requestContext3D();
}

위의 코드는 단순히 Stage3D API를 이용하여 이벤트 리스너를 등록하고 Context3D 인스턴스를 요청합니다. Context3D 인스턴스가 준비되면 이벤트에 의해 initStage3D 함수가 호출됩니다.

initStage3D가 호출되자마자 Context3D::configureBackBuffer 메서드를 호출하여 Context3D를 알맞게 구성하는 것이 중요합니다.

protected function initMolehill(e:Event):void
{
context3D = stage.stage3Ds[0].context3D;
context3D.configureBackBuffer(800, 600, 2, true);
...
}

위의 코드는 여러분이 800 x 600 픽셀의 렌더링 뷰포트를 사용하고, 안티 엘리어싱의 최소 레벨을 2로 지정하고, 이 렌더링 표면에 대해 깊이 버퍼와 스텐실 버퍼를 생성할 것임(네 번째 매개변수)을 나타냅니다.

색깔 있는 삼각형 기하 생성하기

이 절에서는 3D 기하 구조(그려질 3D 물체)를 만듭니다. 여기서는 가장 단순한 기하 구조인 삼각형을 만듭니다.

그러려면 정점 버퍼가 필요하고 정점 위치(x, y, z)와 정점 색상(r, g, b)을 위한 정점 속성을 기술해야 합니다. 각 정점은 속성을 여섯 개 가집니다. 이 정점 버퍼 데이터를 벡터에 담는 것으로 시작합니다.

protected function initMolehill(e:Event):void
{
...
var vertices:Vector.<Number> = Vector.<Number>([
-0.3,-0.3,0, 1, 0, 0, // x, y, z, r, g, b
-0.3, 0.3, 0, 0, 1, 0,
0.3, 0.3, 0, 0, 0, 1]);
...
}

그리고 GPU에 정점 버퍼 데이터를 적재하는 데 쓸 수 있는 VertexBuffer3D 인스턴스를 생성합니다.

protected var vertexbuffer:VertexBuffer3D;
...
protected function initMolehill(e:Event):void
{
...
// Create VertexBuffer3D. 3 vertices, of 6 Numbers each
vertexbuffer:VertexBuffer3D = context3D.createVertexBuffer(3, 6);
// Upload VertexBuffer3D to GPU. Offset 0, 3 vertices
vertexbuffer.uploadFromVector(vertices, 0, 3);
...
}

삼각형을 정의할 인덱스 버퍼도 필요합니다. 이 경우 단일 삼각형은 단순히 정점 0, 1, 2로 구성됩니다. 정점 버퍼와 마찬가지로 인덱스 버퍼도 GPU에 적재되어야 합니다. IndexBuffer3D 클래스를 사용합니다.

protected var indexbuffer:IndexBuffer3D;
...
protected function initMolehill(e:Event):void
{
...
var indices:Vector.<uint> = Vector.<uint>([0, 1, 2]);
// Create IndexBuffer3D. Total of 3 indices. 1 triangle of 3 vertices
indexbuffer = context3D.createIndexBuffer(3);
// Upload IndexBuffer3D to GPU. Offset 0, count 3
indexbuffer.uploadFromVector (indices, 0, 3);
...
}

위 코드는 기하 구조를 정의합니다. 이제 정점 셰이더와 단편 셰이더가 필요합니다. 

간단하게 가기 위해 이 시리즈의 앞선 글인 AGAL이란에서 설명한 셰이더 프로그램을 그대로 쓰겠습니다. 정점 셰이더는 액션스크립트로부터 받은 변환 행렬을 가지고 정점들을 변환하고 정점 색상은 그저 렌더링 파이프라인을 따라 단편 셰이더로 전송합니다.

m44 op, va0, vc0
mov v0, va1

단편 셰이더는 보간된 색상을 입력으로 받아 그대로 출력합니다.

mov oc, v0

AGAL 미니 어셈블러로 셰이더 코드를 목적 코드로 편집하고, API의 Program3D 클래스로 GPU에 셰이더를 적재합니다.

protected function initMolehill(e:Event):void
{
...
var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
"m44 op, va0, vc0\n" + // pos to clipspace
"mov v0, va1" // copy color
);

var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
"mov oc, v0 "
);

program = context3D.createProgram();
program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
...
}
 
장면 그리기

장면을 렌더링할 준비는 되었습니다. 이 절에서는 렌더링 루프를 구성합니다. ENTER_FRAME 이벤트에 의해 매 프레임마다 호출되는 onRender 함수를 만듭니다.

protected function onRender(e:Event):void
{
if ( !context3D ) 
return;
...
}
 
렌더링의 시작으로 Context3D::clear를 호출합니다. 이 메서드는 렌더링 색상 버퍼(내용이 그려지는 표면)을 우리가 지정한 배경 색상으로 칠합니다(이 Context3D 객체와 연관된 깊이 버퍼와 스텐실 버퍼 역시 청소됩니다). 밑의 코드로 하얀 배경을 칠합니다.

protected function onRender(e:Event):void
{
...
context3D.clear ( 1, 1, 1, 1 );
...
}

프레임마다 필요한 정점 버퍼와 셰이더 프로그램을 지정합니다.

정점 셰이더에서 쓸 변환 행렬도 넘겨야 합니다. 프레임마다 변하는 회전 행렬을 써서 삼각형이 조금씩 돌아가게 해봅시다...

protected function onRender(e:Event):void
{
...
// vertex position to attribute register 0
context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// color to attribute register 1
context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
// assign shader program
context3D.setProgram(program);

var m:Matrix3D = new Matrix3D();
m.appendRotation(getTimer()/40, Vector3D.Z_AXIS);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
...
}

다음은 실제로 렌더링을 해볼 시간입니다. Context3D::drawTriangles 메서드를 인덱스 버퍼를 넘겨 호출합니다. 이 명령은 삼각형을 렌더링 표면(색상 버퍼)에 그립니다.

마지막으로, 그 프레임에 모든 3D 물체를 그리는 작업이 끝났으면 Context3D::present를 호출합니다. 이 메서드는 그 프레임에 그릴 건 다 그렸고 화면에 보여줄 준비가 되었다는 것을 앱이 가진 Stage3D에 알려줍니다.

protected function onRender(e:Event):void
{
...
context3D.drawTriangles(indexbuffer);
context3D.present();
}

Hello Triangle Colored 애플리케이션을 실행하여 결과물을 보고 잠시 즐기세요(그림 2).

Figure 2. The completed Hello Triangle Colored application.

그림 2. 완성된 Hello Triangle Colored 애플리케이션.

이것은 Hello Triangle Colored 애플리케이션의 전체 코드입니다. 

package
{
import com.adobe.utils.AGALMiniAssembler;
import flash.display.Sprite;
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.events.Event;
import flash.geom.Matrix3D;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.utils.getTimer;
[SWF(width="800", height="600", frameRate="60", backgroundColor="#FFFFFF")]
public class HelloTriangleColored extends Sprite
{
protected var context3D:Context3D;
protected var program:Program3D;
protected var vertexbuffer:VertexBuffer3D;
protected var indexbuffer:IndexBuffer3D;
public function HelloTriangleColored()
{
stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initMolehill );
stage.stage3Ds[0].requestContext3D();
addEventListener(Event.ENTER_FRAME, onRender);
}
protected function initMolehill(e:Event):void
{
context3D = stage.stage3Ds[0].context3D;
context3D.configureBackBuffer(800, 600, 1, true);
var vertices:Vector.<Number> = Vector.<Number>([
-0.3,-0.3,0, 1, 0, 0, // x, y, z, r, g, b
-0.3, 0.3, 0, 0, 1, 0,
0.3, 0.3, 0, 0, 0, 1]);
// Create VertexBuffer3D. 3 vertices, of 6 Numbers each
vertexbuffer = context3D.createVertexBuffer(3, 6);
// Upload VertexBuffer3D to GPU. Offset 0, 3 vertices
vertexbuffer.uploadFromVector(vertices, 0, 3);
var indices:Vector.<uint> = Vector.<uint>([0, 1, 2]);
// Create IndexBuffer3D. Total of 3 indices. 1 triangle of 3 vertices
indexbuffer = context3D.createIndexBuffer(3);
// Upload IndexBuffer3D to GPU. Offset 0, count 3
indexbuffer.uploadFromVector (indices, 0, 3);
var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
"m44 op, va0, vc0\n" + // pos to clipspace
"mov v0, va1" // copy color
);
var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
"mov oc, v0"
);
program = context3D.createProgram();
program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
}
protected function onRender(e:Event):void
{
if ( !context3D ) 
return;
context3D.clear ( 1, 1, 1, 1 );
// vertex position to attribute register 0
context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// color to attribute register 1
context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
// assign shader program
context3D.setProgram(program);
var m:Matrix3D = new Matrix3D();
m.appendRotation(getTimer()/40, Vector3D.Z_AXIS);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
context3D.drawTriangles(indexbuffer);
context3D.present();
}
}
}

텍스처 맵핑 적용하기

Hello Triangle Colored 애플리케이션은 색칠된 삼각형을 그립니다. 삼각형의 색상은 정점 버퍼의 일부인 정점 속성으로서 기술됩니다. 이 삼각형은 색칠된 기하 구조의 예시고 색상은 정점별로 기술됩니다.

이 절에서는 기하 구조를 그리는 다른 방법으로, 흔히 텍스처 맵핑이라 부르는 기법을 탐구합니다. 텍스처 맵핑은 이미지(텍스처)를 기하 구조에 적용하는 작업입니다. 이 텍스처 이미지를 그림이 있는 종이 조각이나 월페이퍼의 일부 같은 것으로 생각해도 좋습니다. 삼각형(보다 일반적으로는 3D 물체)을 기술하고 물체의 표면에 그림 종이를 감쌉니다.

이 전략을 사용하여 3D 물체가 그림 텍스처의 모든 세부 사항을 정말로 포함하는 것처럼 보이게 할 수 있습니다. 실제로는 텍스처가 기하 구조의 일부가 아닙니다. 눈으로 보이는 복잡함은 단지 적용된 텍스처 이미지에서 기인할 뿐입니다.

텍스처 맵핑을 적용할 때는 기하 구조에서 텍스처 요소가 놓일 정확한 위치를 명시합니다. 기본적으로 기하 구조를 텍스처 이미지로 감쌀 때는 텍스처 이미지의 각 픽셀이 3D 기하 구조의 어디로 떨어질지를 정확히 기술하는 정교한 맵핑을 해야 합니다.

UV 좌표 다루기
 
텍스처를 3D 기하 구조에 늘어놓는 방법은 정점마다 맵핑을 기술하는 것입니다. 여러분은 정점마다 (U, V)로 표현되는 2D 좌표를 명시합니다. 이 좌표는 그 정점과 대응되는 텍스처 이미지의 한 점을 정의합니다. 이들 UV 좌표가 정점 버퍼의 정점 속성으로 기입되고 정점 셰이더는 이것들을 입력 스트림으로 받습니다.

그러면 정점 속성에 대해 흔히 하듯이 정점 셰이더는 UV 좌표들을 렌더링 파이프라인을 따라 그대로 내보내고 래스터라이저가 이것들을 보간합니다(자세한 것은 이 시리즈의 정점 셰이더와 픽셀 셰이더라는 이전 글을 보세요). 픽셀 셰이더는 삼각형의 각 픽셀에 적합한 UV 좌표값을 받습니다. 그려지는 각각의 삼각형의 각각의 픽셀은 텍스처의 특정 픽셀(텍스처 요소 또는 텍셀이라고도 합니다)로 덮어집니다.

달리 말하자면 UV 좌표를 기술하여 3D 기하 구조와 텍스처 이미지 사이의 맵핑을 만든 것입니다. 이것이 텍스처 맵핑이라는 용어에 숨은 개념입니다.

Stage3D API의 Texture 클래스 다루기
 
Texture 클래스는 텍스처 적용을 지원합니다.

텍스처 이미지는 렌더링 도중에 쓸 수 있게 먼저 GPU 메모리에 적재되어야 합니다. 다음 코드로 텍스처 이미지를 GPU에 적재할 수 있습니다.

protected var texture:Texture;
...

protected function initMolehill(e:Event):void
{
...

var bitmap:Bitmap = new TextureBitmap();
texture = context3D.createTexture(bitmap.bitmapData.width, bitmap.bitmapData.height, Context3DTextureFormat.BGRA, false);

texture.uploadFromBitmapData(bitmap.bitmapData);
...
}

앞서 설명했듯이 정점 셰이더는 UV 텍스처 좌표를 정점 속성으로서 받아 렌더링 파이프라인을 따라 흘려보내고 알맞게 보간되어 단편 셰이더에 넘겨지도록 합니다. 정점 셰이더는 앞의 색칠된 삼각형 예제의 것과 아주 유사하지만 속성 레지스터 1에 색상값이 아니라 UV 좌표가 들어갑니다.

m44 op, va0, vc0
mov v0, va1

단편 셰이더는 넘겨받은 보간된 UV 좌표를 가지고 텍스처 추출기를 통해 텍스처를 추출합니다.

텍스처가 텍스처 추출기 0과 연관되었다고 상상합시다. 이 경우 단편 셰이더는 아래처럼 됩니다.

tex ft1, v0, fs0 <2d>
mov oc, ft1

첫 번째 줄은 텍스처 추출기 0과 변형(varying) 레지스터 0의 UV 좌표를 써서 텍스처를 추출합니다. 그리고 그 결과를 임시 레지스터 1에 복사합니다. 두 번째 줄은 임시 레지스터 1의 내용(추출된 텍스처)을 출력에 복사합니다.

Hello Triangle Colored 애플리케이션을 텍스처 맵을 적용하도록 고치기
 
이 절에서는 앞의 애플리케이션을 수정하여 텍스처 맵핑을 사용하도록 만들겠습니다.

애플리케이션에서 처음으로 손볼 것은 텍스처를 위한 이미지를 추가하는 것입니다. 밑의 코드로 외부 이미지를 들여옵니다.

[Embed( source = "RockSmooth.jpg" )]
protected const TextureBitmap:Class;
...

정점 버퍼의 정의도 바꿔서 색상 대신 UV 좌표를 정점 속성으로서 넘깁니다.

protected function initMolehill(e:Event):void
{
...
var vertices:Vector.<Number> = Vector.<Number>([
-0.3,-0.3,0, 1, 0, // x, y, z, u, v
-0.3, 0.3, 0, 0, 1,
0.3, 0.3, 0, 1, 1]);
// Create VertexBuffer3D. 3 vertices, of 5 Numbers each
vertexbuffer = context3D.createVertexBuffer(3, 5);
// Upload VertexBuffer3D to GPU. Offset 0, 3 vertices
vertexbuffer.uploadFromVector(vertices, 0, 3);
...
}

U
V 좌표는 0과 1 사이에서 정의되며 (U, V) = (0. 0)은 텍스처 이미지의 왼쪽 밑 모서리를, (U, V) = (1, 1)은 오른쪽 위 모서리를 뜻하는 것에 주의하세요.

렌더링 루프는 Texture 객체를 텍스처 추출기 0과 연결합니다.

protected function onRender(e:Event):void
{
...
// assign texture to texture sampler 0
context3D.setTextureAt(0, texture);
...
}

이제 애플리케이션을 실행해보면 텍스처를 입힌 삼각형이 표시됩니다(그림 3).

Figure 3. The completed Hello Triangle Textured application.
그림 3. 완성된 Hello Triangle Textured 애플리케이션.
 
밑은 전체 코드입니다. 

package
{
import com.adobe.utils.AGALMiniAssembler;
import flash.display.Bitmap;
import flash.display.Sprite;
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.Matrix3D;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.utils.getTimer;
[SWF(width="800", height="600", frameRate="60", backgroundColor="#FFFFFF")]
public class HelloTriangleTextured extends Sprite
{
[Embed( source = "RockSmooth.jpg" )]
protected const TextureBitmap:Class;

protected var texture:Texture;
protected var context3D:Context3D;
protected var program:Program3D;
protected var vertexbuffer:VertexBuffer3D;
protected var indexbuffer:IndexBuffer3D;
public function HelloTriangleTextured()
{
stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initMolehill );
stage.stage3Ds[0].requestContext3D();
addEventListener(Event.ENTER_FRAME, onRender);
}
protected function initMolehill(e:Event):void
{
context3D = stage.stage3Ds[0].context3D;
context3D.configureBackBuffer(800, 600, 1, true);
var vertices:Vector.<Number> = Vector.<Number>([
-0.3,-0.3,0, 1, 0, // x, y, z, u, v
-0.3, 0.3, 0, 0, 1,
0.3, 0.3, 0, 1, 1]);
// Create VertexBuffer3D. 3 vertices, of 5 Numbers each
vertexbuffer = context3D.createVertexBuffer(3, 5);
// Upload VertexBuffer3D to GPU. Offset 0, 3 vertices
vertexbuffer.uploadFromVector(vertices, 0, 3);
var indices:Vector.<uint> = Vector.<uint>([0, 1, 2]);
// Create IndexBuffer3D. Total of 3 indices. 1 triangle of 3 vertices
indexbuffer = context3D.createIndexBuffer(3);
// Upload IndexBuffer3D to GPU. Offset 0, count 3
indexbuffer.uploadFromVector (indices, 0, 3);
var bitmap:Bitmap = new TextureBitmap();
texture = context3D.createTexture(bitmap.bitmapData.width, bitmap.bitmapData.height, Context3DTextureFormat.BGRA, false);
texture.uploadFromBitmapData(bitmap.bitmapData);
var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
"m44 op, va0, vc0\n" + // pos to clipspace
"mov v0, va1" // copy UV
);
var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
"tex ft1, v0, fs0 <2d>\n" +
"mov oc, ft1"
);
program = context3D.createProgram();
program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
}
protected function onRender(e:Event):void
{
if ( !context3D ) 
return;
context3D.clear ( 1, 1, 1, 1 );
// vertex position to attribute register 0
context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// UV to attribute register 1
context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
// assign texture to texture sampler 0
context3D.setTextureAt(0, texture);
// assign shader program
context3D.setProgram(program);
var m:Matrix3D = new Matrix3D();
m.appendRotation(getTimer()/40, Vector3D.Z_AXIS);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
context3D.drawTriangles(indexbuffer);
context3D.present();
}
}
}

이제 어디로 갈까

이 시리즈의 앞선 글들에서 배운 개념을 써서 마침내 핵심 공정을 파헤치고 Stage3D에 기반하여 완벽히 작동하는 액션스크립트 애플리케이션을 두 개 제작했습니다. 장면은 무척이나 단순했지만 여기에는 Stage3D를 다룰 때 필요한 모든 개념이 들어있습니다. 이제 Stage3D 앱을 만들면 만들 수록 상황이 더 풍부하고 재밌어질 것입니다.

다음 글에서는 3D 렌더링의 주요 화제인 원근감을 배웁니다.

그 동안 개발자 센터에서 더 많은 튜토리얼과 예시 프로젝트를 찾아보세요.
Comments