Olá pessoal, tudo certo?
No post de hoje mostro um exemplo clássico de computação gráfica: um cubo 3D que pode ser rotacionado, transladado e escalado.
Para entender o post de hoje, é fundamental que você tenha algum entendimento sobre utilização com 3D com XNA. Por isso, recomendo fortemente que, antes de ler esse post, dê uma olhada no post anterior dessa série. Se você nunca escreveu nada com XNA, talvez deseje dar uma olhada nos posts dessa série desde seu início.
Como de prática, todo o código-fonte demonstrado hoje está disponível no GitHub da série em http://github.com/ElemarJR/VamosAprenderXNA
Informação para desenhar um cubo
Lembra como desenhamos um “retângulo 3D”?! Pela combinação de dois triângulos. Logo, como será que desenhamos um Cubo? Combinando diversos retângulos. Repare o código que segue:
VertexPositionColor[] verts;
VertexBuffer vertexBuffer;
BasicEffect effect;
Camera camera;
protected override void LoadContent()
{
camera = new Camera(this, new Vector3(0, 0, 5));
Components.Add(camera);
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
var tlf = new Vector3(-1.0f, 1.0f, -1.0f);
var blf = new Vector3(-1.0f, -1.0f, -1.0f);
var trf = new Vector3( 1.0f, 1.0f, -1.0f);
var brf = new Vector3( 1.0f, -1.0f, -1.0f);
var tlb = new Vector3(-1.0f, 1.0f, 1.0f);
var trb = new Vector3( 1.0f, 1.0f, 1.0f);
var blb = new Vector3(-1.0f, -1.0f, 1.0f);
var brb = new Vector3( 1.0f, -1.0f, 1.0f);
verts = new [] {
// front
new VertexPositionColor(tlf, Color.Red),
new VertexPositionColor(blf, Color.Red),
new VertexPositionColor(trf, Color.Red),
new VertexPositionColor(blf, Color.Red),
new VertexPositionColor(brf, Color.Red),
new VertexPositionColor(trf, Color.Red),
// back
new VertexPositionColor(tlb, Color.Blue),
new VertexPositionColor(trb, Color.Blue),
new VertexPositionColor(blb, Color.Blue),
new VertexPositionColor(blb, Color.Blue),
new VertexPositionColor(trb, Color.Blue),
new VertexPositionColor(brb, Color.Blue),
// top
new VertexPositionColor(tlf, Color.Green),
new VertexPositionColor(trb, Color.Green),
new VertexPositionColor(tlb, Color.Green),
new VertexPositionColor(tlf, Color.Green),
new VertexPositionColor(trf, Color.Green),
new VertexPositionColor(trb, Color.Green),
// bottom
new VertexPositionColor(blf, Color.Yellow),
new VertexPositionColor(blb, Color.Yellow),
new VertexPositionColor(brb, Color.Yellow),
new VertexPositionColor(blf, Color.Yellow),
new VertexPositionColor(brb, Color.Yellow),
new VertexPositionColor(brf, Color.Yellow),
// left
new VertexPositionColor(tlf, Color.Purple),
new VertexPositionColor(blb, Color.Purple),
new VertexPositionColor(blf, Color.Purple),
new VertexPositionColor(tlb, Color.Purple),
new VertexPositionColor(blb, Color.Purple),
new VertexPositionColor(tlf, Color.Purple),
// right
new VertexPositionColor(trf, Color.Magenta),
new VertexPositionColor(brf, Color.Magenta),
new VertexPositionColor(brb, Color.Magenta),
new VertexPositionColor(trb, Color.Magenta),
new VertexPositionColor(trf, Color.Magenta),
new VertexPositionColor(brb, Color.Magenta),
};
vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor),
verts.Length, BufferUsage.None);
vertexBuffer.SetData(verts);
effect = new BasicEffect(GraphicsDevice);
}
O que fizemos?
- definimos um conjunto básico de vértices representando os “quatro” extremos de nosso retângulo;
- utilizando esses “quatro vértices fundamentais”, criamos os “dois triângulos” de cada lado do cubo;
- utilizamos uma cor para cada lado;
- inicializamos um objeto câmera (demonstrado no post anterior)
- inicializamos um “VertexBuffer” para armazenar todo o conjunto.
Desenhando o Cubo 3D
Uma das belezas do XNA é que ele facilita o desenvolvimento de atividades complexas. Aliás, grandes parcelas de código permanecem “imutáveis” porque seu funcionamento é influenciado pelo conjunto de dados de entrada. Um ótimo exemplo disso são as rotinas de desenho. Observe o código que segue:
Matrix worldRotation = Matrix.Identity;
Matrix worldTranslation = Matrix.Identity;
Matrix worldScale = Matrix.Identity;
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.SetVertexBuffer(vertexBuffer);
effect.World = worldScale * worldRotation * worldTranslation;
effect.View = camera.View;
effect.Projection = camera.Projection;
effect.VertexColorEnabled = true;
foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList,
verts, 0, 12);
}
base.Draw(gameTime);
}
Essa rotina é basicamente idêntica a que utilizamos para desenhar um triângulo ou um retângulo. Como estamos utilizando um conjunto maior de dados temos um desenho diferente.
Já temos condições de executar o programa. O resultado obtido é ilustrado na figura que segue:
Basicamente, estamos vendo a face frontal do cubo. Legal né?! Vamos adicionar alguma rotação?
Permitindo que o usuário rotacione o Cubo 3D
Se você observou nossa rotina de desenho, perceberá que utilizamos três matrizes para definir o mundo. Mostrei o conceito no post anterior. Vamos alterar nosso “Update” para “animar as coisas”.
float rotationStep = MathHelper.PiOver4 / 30;
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
if (Keyboard.GetState().IsKeyDown(Keys.Left))
worldRotation *= Matrix.CreateRotationY(-rotationStep);
else if (Keyboard.GetState().IsKeyDown(Keys.Right))
worldRotation *= Matrix.CreateRotationY(rotationStep);
if (Keyboard.GetState().IsKeyDown(Keys.Up))
worldRotation *= Matrix.CreateRotationX(-rotationStep);
else if (Keyboard.GetState().IsKeyDown(Keys.Down))
worldRotation *= Matrix.CreateRotationX(rotationStep);
if (Keyboard.GetState().IsKeyDown(Keys.L))
worldTranslation *= Matrix.CreateTranslation(new Vector3(-0.01f, 0, 0));
else if (Keyboard.GetState().IsKeyDown(Keys.R))
worldTranslation *= Matrix.CreateTranslation(new Vector3(0.01f, 0, 0));
if (Keyboard.GetState().IsKeyDown(Keys.T))
worldTranslation *= Matrix.CreateTranslation(new Vector3(0, 0.01f, 0));
else if (Keyboard.GetState().IsKeyDown(Keys.B))
worldTranslation *= Matrix.CreateTranslation(new Vector3(0, -0.01f, 0));
if (Keyboard.GetState().IsKeyDown(Keys.I))
worldScale *= Matrix.CreateScale(1.01f);
else if (Keyboard.GetState().IsKeyDown(Keys.O))
worldScale *= Matrix.CreateScale(0.99f);
base.Update(gameTime);
}
Pronto! Estou monitorando algumas teclas e alterando as matrizes de translação, rotação e escala conforme essas são pressionadas.
Brincando um pouco com o programa, temos:
Lindo!
Por hoje, era isso!
![]()






Daniel Moreira Yokoyama
11/04/2011
Muito bom, Elemar.
Até aqui já fiquei super inspirado pra fazer um cubo-mágico.
Mal posso esperar pra entender melhor a dinâmica do uso da câmera. Já vi que vou ter que voltar a estudar matrizes… não que eu não goste, era até divertido… mas já faz teeeeeeeeempo.
Parabéns pelo blog.
elemarjr
11/04/2011
Obrigado pelos elogios.
XNA facilita muito o desenvolvimento exatamente por permitir que você vá “adiando” essa volta aos estudos.
Quanto a dinâmica da câmera, ficará mais compreensível nos próximo posts.