A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Curso Professional Lucas Euzébio Machado.

Apresentações semelhantes


Apresentação em tema: "Curso Professional Lucas Euzébio Machado."— Transcrição da apresentação:

1 Curso Professional Lucas Euzébio Machado

2 Tópicos do Curso C++, Algoritmos e Estruturas de Dados (36)
Computação Gráfica (36) Programação Distribuída (6) Física (6) Inteligência Artificial (6) Fundamentos de Programação de Jogos (6) Projeto Final (24)

3 Tópicos para C++, Algoritmos e Estruturas de Dados
Variáveis, Ponteiros, Referência, Alocação e Liberação de Memória Testes de condições Loops Funções Módulos, Compilação e Link-Edição Orientação a Objetos Classes e Structs Encapsulamento, Herança e Polimorfismo Construtores e Destrutores Exceções Funções e Variáveis estáticas Recursão Lista Encadeada, Fila e Pilha Árvore Binária Grafo Hashtable Templates Operator Overloading Lendo e Gravando Arquivos Multithreading

4 Variáveis

5 Ponteiros Ponteiros são variáveis que armazenam endereços de memória.

6 Variáveis de referência
Referências servem como um segundo nome para um objeto. Elas permitem acesso indireto de forma semelhante a ponteiros.

7 Alocação de Memória Para alocar memória da heap:

8 Liberando memória

9 Vetores Vetores são agregações de elementos de um mesmo tipo que ocupam a memória de forma sequencial e adjacente.

10 Vetores Uma variável vetor armazena na realidade o endereço de memória do primeiro elemento do vetor.

11 Vetores Vetores podem ser multidimensionais

12 Testes de Condições

13 Testes de Condições

14 Loops Instruções usada para causar repetições de trechos de código

15 Loops

16 Exercício Crie um programa que gera um buffer de inteiros de 10 posições. Cujos valores sofrem um decremento de 10 até 1. Adicione um trecho de código que busca pelo numero 4 no vetor e salva o endereço da posição do vetor em um ponteiro.

17 Exercício Crie um programa que crie uma matriz 4x4 identidade usando um buffer bidimensional.

18 Funções Funções são subprogramas que realizam operações definidas por usuários. Ferramenta fundamental para redução de complexidade e reuso de código. Em qualquer projeto, funções serão utilizadas sem que se conheça suas implementações internas.

19 Funções

20 Exercício Crie uma função que adiciona dois vetores de 4 elementos e retorna o resultado em um terceiro vetor

21 Módulos, Compilação e Link-Edição
Módulos permitem a divisão de um sistema em diferentes partes, cada uma responsável por uma tarefa específica. Um módulo para nós será definido como um .h e um .cpp. O arquivo .h possui declarações. O arquivo .cpp possui implementações.

22 Exemplo de um modulo Vector3.h Vector3.cpp

23 Usando um módulo

24 Usando Módulos Ao usar include é incluído todo o arquivo de texto na linha chamada.

25 Compilação Ao compilar um arquivo de implementação .cpp é gerado um arquivo .obj com código binário e tabelas com referências externas ainda não resolvidas e símbolos públicos.

26 Link-Edição A link edição resolve referências externas e junta todo o código em um só código binário gerando assim o arquivo executável.

27 Exercício Implemente um módulo Vector4.h e Vector4.cpp que possui as funções de adição, subtração e multiplicação de vetores de 4 elementos e salvando o resultado em um outro vetor. Crie um arquivo .cpp com a main e inclua o header do Vector4.h. Compile ambos arquivos .cpp e gere o executável.

28 Orientação a Objeto Um objeto é uma entidade(podendo se basear em algo concreto ou abstrato) que possui um comportamento ou representa algo. Você interage com um objeto através de uma interface formada por funções. Isso permite uma interação bem comportada. A divisão de um sistema em objetos permite uma enorme redução em complexidade e aumento em reusabilidade.

29 Exemplo de um sistema dividido em objetos

30 Classes Objetos são representados em software através de classes

31 Encapsulamento Apenas os membros públicos de uma função podem ser acessados por outros objetos.

32 Encapsulamento Fundamental para redução de complexidade.
Você chama um método de uma classe e um problema é resolvido para você. Não há necessidade de saber como o problema foi resolvido. Permite interação comportada. Como você controla o acesso às variáveis através de funções não é possível partes externas do programa agirem de forma errada com suas variáveis.

33 Exercício Implemente a classe AIManager. Essa classe possui duas funções. AddObject e Think. A função AddObject recebe uma string com um nome, uma posição x e uma posição y. A função Think mostra na tela os objetos adicionados.

34 Herança Certos objetos compartilham características porém possuem particularidades suficientes de forma que precisam de novos objetos para serem apropriadamente representados.

35 Herança

36 Herança O objeto Person e o objeto Stone são ambos objetos de cena que podem ser desenhados na tela. Então ambos podem pertencer à mesma classe de objetos SceneObject. Se por exemplo um SceneObject possuir uma posição 3d. Tanto Person quanto Stone possuirão essa variável de posição pois a posição é uma variável que existe em todo objeto da classe SceneObject.

37 Herança

38 Exercício Reimplemente o exercício anterior fazendo o AIManager ter funções para adicionar Dragon e Orc que são classes derivadas de AIEntity. A classe AIEntity possui membros públicos para armazenar um nome e uma posição x e y. A classe Dragon tem uma string com o tipo de baforada do dragão e a classe Orc possui um inteiro que indica a arma sendo usada. Implemente a função Think para tratar dos tipos específicos e desenhar suas partes específicas.

39 Polimorfismo Quando um conjunto de classes deriva de uma mesma classe base, todo o conjunto compartilha uma interface. É possível escrever partes de código levando em consideração apenas classes base de determinados objetos. Isso é vantajoso para aumentar o reúso no código.

40 Polimorfismo

41 Polimorfismo

42 Exercício Reimplemente o AIManager para receber objetos AIEntity. Crie uma função virtual pura em AIEntity chamada Think que imprime os dados da entidade. Faça a classe Orc e Dragon implementarem a função Think. Mude o AIManager para fazer uso das funções Think dos objetos.

43 Construtores e Destrutores
Construtores são métodos de uma classe chamada para “construir” a classe. Todo objeto criado tem seu construtor chamado.

44 Construtores e Destrutores
É possível definir diferentes tipos de construtores

45 Construtores e Destrutores
Destrutores fazem o trabalho inverso dos construtores. Eles fazem a destruição do objeto, liberando recursos adquiridos como memória alocada, arquivos abertos, etc.

46 Construtores e Destrutores
Em classes base use destrutores virtuais. Ao deletar um objeto usando um ponteiro base o comportamento é indefinido caso o destrutor da classe base não seja virtual.

47 Exceções Eventos anormais são normais. 
Programas executam normalmente até que algum problema ocorra como falta de memória, acesso à uma área de memória ilegal, etc etc etc etc etc etc etc etc etc etc..... É possível testar esses problemas ou exceções usando ifs ou fazendo uso de ferramentas de C++ criadas específicamente para tratar de exceções.

48 Exceções Ao detectar uma anomalia, você gera uma exceção através da operação throw.

49 Exceções Um exceção é uma classe

50 Exceções Para capturar uma exceção deve-se usar o operador try e catch.

51 Exercício Implemente uma classe chamada Buffer que cria um buffer de ints. A classe possui um construtor que recebe o número de elementos, e um destrutor que destroi os elementos. A classe possui também um função Add que recebe um vetor de ints pra adicionar ao buffer e uma função Remove que recebe n e remove n elementos de buffer. Jogue exceções caso sejam adicionados mais elementos do que o Buffer suporta ou sejam removidos mais elementos do que existem no buffer.

52 Funções e Variáveis Estáticas
Apesar de todo o perigo, às vezes acesso à informações globais é necessário e até simplifica designs. Todos os objetos com um membro estático compartilham a mesma instância desse membro. Ou seja, se um objeto altera a variável estática, isso será visível por todos os outros objetos da mesma classe.

53 Declarando membros estáticos

54 Usando membros estáticos

55 Usando membros estáticos

56 Variáveis estáticas e constantes

57 Exercício Implemente uma classe que possui internamente uma variável estática ponteiro do tipo da própria classe. Inicialize essa variável para NULL Implemente uma função estática da classe chamada GetInstance que retorna a variável quando o ponteiro é diferente de NULL, se o ponteiro for NULL é alocada memória para a variável e seu endereço é armazenado no ponteiro.

58 Recursão A recursão é uma forma de solucionar certos problemas que envolve fazer uma função chamar a si mesma para resolver o problema.

59 Exemplo

60 Exercício Implemente uma função recursiva que busque em um vetor de inteiros ordenado crescentemente por um determinado valor. A função deve retornar o índice do valor ou , -1 caso o valor não seja encontrado.

61 Busca Binária

62 Complexidade de Algoritmos
Complexidade não é de difícil de entender ... é de difícil de computar. É nossa missão encontrar os algoritmos mais eficientes para resolver algum problema. Os algoritmos mais eficientes para resolver algum problema são chamados de algoritmos ótimos.

63 Complexidade da Busca Linear
No pior caso, é necessário realizar n testes para achar (ou não) um valor. É usada uma notação O(n) para representar o algoritmo busca linear. O(n) ~= se você colocar n elementos como input, o custo da função é uma função linear.

64 Complexidade da Busca Binária
Teste 1 = n elementos Teste 2 = n / 2 elementos Teste 3 = n / 4 elementos Teste final quando você tem 1 elemento. Dado n elementos, quantos testes são feitos? Temos que achar i quando n/2^i = 1. n/2^i = 1 -> n = 2^i log n = log 2^i Log n = i Complexidade = O(log n) Busca binária é melhor que busca linear.

65 Lista Encadeada, Fila e Pilha
As vezes, não é possível saber o número máximo de elementos que serão adicionados à uma classe. Quando isso acontece é necessário criar uma estrutura capaz de crescer de acordo com a necessidade. Lista Encadeada, Fila e Pilha são estruturas de dados fundamentais usadas em diversos algoritmos.

66 Lista Encadeada A lista encadeada é uma lista criada dinamicamente que se liga através de ponteiros.

67 Lista Encadeada - implementação
Uma classe consiste na lista em si. A classe da lista usa outra classe que representa os nós da lista. Cada nó armazena os dados e o ponteiro para o próximo nó da lista. O último nó aponta para NULL. A classe da lista aponta para o primeiro elemento da lista.

68 Exercício Implemente uma lista encadeada cujos nós armazenam posições x,y e z em floats e possuem também um id inteiro. Crie um método na classe para adicionar elementos no final da lista. Crie método na classe para buscar um nó dado um inteiro, o método deve retornar as posições x, y e z. Crie um método para imprimir toda a lista.

69 Lista Encadeada

70 Lista Encadeada

71 Fila Implementada de maneira semelhante à lista encadeada, porém elementos só podem ser inseridos no final e removidos na frente. Funciona em forma FIFO (First In First Out). Imagine uma fila de cinema!

72 Pilha Também implementada de maneira parecida com a lista encadeada porém sua operação é LIFO (Last In First Out). A pilha mantém um ponteiro para o seu topo. Quando se deseja adicionar um elemento realiza-se um push adicionando um elemento ao topo da lista, quando se deseja remover um elemento, faz-se um pop, removendo o elemento do topo.

73 Exercício Implemente uma pilha. A pilha possui um ponteiro chamado top que aponta para o topo da pilha. Crie o método push que adiciona um elemento ao topo da pilha. Crie o método pop que remove o elemento do topo da pilha. Crie o método print para imprimir a pilha.

74 Árvore Binária de Busca
Estrutura de dados semelhante à uma arvore invertida onde cada nó possui até 2 filhos. Cada nó possui um id, um ponteiro para o filho esquerdo e um ponteiro para o filho direito. Cada nó possui a seguinte característica, o filho a esquerda possui um id menor que o id do nó e o filho a direita possui um id maior que o id do nó.

75 Árvore Binária de Busca
A busca por um valor possui um custo O(h). Se a árvore estiver desbalanceada isso pode representar O(n) Se a árvore estiver balanceada isso pode representar O(log n) Uma árvore balanceada é uma árvore em que para todo nó, suas subárvores à esquerda e à direita diferem no máximo em 1 na altura. Considere uma subárvore vazia como de altura -1. Folha é um nó sem filhos.

76 Árvore Binária de Busca
Através de uma árvore balanceada é possível achar uma árvore perfeita de mesma altura. Árvore perfeita é uma árvore em que todas as folhas possuem a mesma altura e todos os nós possuem 2 ou 0 filhos. Número de elementos da árvore completa = Somatório(2^i, i varia de 0 até a altura). n = 1 * (2^(h+1) – 1) / 2 – 1 (somatório de progressão geométrica) n = 2^(h+1) – 1 n + 1 = 2^(h+1) log(n+1) = h + 1 log(n+1) – 1 = h Logo a altura de uma árvore balanceada é um logaritmo de n ... O que é muito bom!!!!

77 Exercício Implemente uma árvore binária onde os nós possuem um id inteiro e armazenam um valor inteiro. Implemente o método de busca por um id. Implemente o método de busca pelo maior elemento. Implemente o método de adição de elementos. Implemente o método que desenha na tela os valores dos nós de maneira ordenada crescentemente por id.

78 Grafo Grafos são uma estrutura de dados onde um nó pode apontar para um número qualquer de outros nós. Grafos podem ser cíclicos se possuirem referências que façam um ciclo (como um filho apontando para um pai). Caso não existam referências cíclicas, o grafo é definido acíclico e se assemelha à uma árvore n-ária, onde cada nó possui n filhos.

79 Grafos Acíclicos – Uma Implementação
Cada grafo possui um ponteiro para o pai e uma estrutura de dados(lista encadeada, vetor) que armazena os ponteiros para os filhos. Se assemelha a uma árvore n-ária.

80 Exercício Implemente um grafo acíclico que armazena seus filhos em uma lista encadeada. Para isso implemente uma classe GraphNode que possui os métodos AddChild e Print. Coloque os GraphNodes possuindo um inteiro internamente. AddChild adiciona um filho ao nó. Print imprime os nós do grafo. Construa um grafo e o imprima.

81 Hash Table É uma estrutura de dados que permite acessos em O(1) .... Ou seja ... Muito rápidos. Para adicionar um elemento, é utilizada uma chave que identifica o elemento. Através dessa chave, é calculada a posição do elemento (essa posição geralmente é um índice de um vetor). A função que pega essa chave e calcula uma posição é chamada de função de hash.

82 Hash Table - Exemplo Para armazenar objetos é usado um vetor onde cada elemento desse vetor é um ponteiro para um objeto. A chave é um inteiro. A posição é calculada achando o resto da divisão da chave pelo tamanho do vetor de ponteiros. ( index = chave % tam_vec; ) Quando uma chave gerar uma posição que já contém um objeto, outra estrutura de dados é utilizada para salvar os objetos (lista encadeada, arvore binária de busca, outra lista encadeada).

83 Exercício Implemente uma hash table com as mesmas características do exemplo anterior. Para salvar objetos na mesma posição do vetor de ponteiros utilize uma lista encadeada. Crie uma função de inserção que recebe a chave e o objeto e o salva na hash table. Crie uma função de search que procura pelo objeto dada uma chave.

84 Template Templates são como receitas para criar classes que recebem um ou mais tipos como parâmetros. São úteis para reaproveitar algoritmos em diferentes tipos de dados.

85 Template

86 Template Toda vez que um template é utilizado para um determinado tipo, é criado o código da classe para fazer uso daquele tipo. Então no exemplo anterior, são criadas 2 classes com a mesma lógica porém só com os tipos int e float diferentes. Como as classes são geradas em tempo de compilação, é necessário saber a implementação de todos os métodos de cada template. Se um template define seus métodos em um .cpp, outras classes não serão capazes de ver essa implementação e criar as novas classes (você provavelmente terá problemas de link-edição). Por esse motivo, geralmente os templates são criados completamente no .h (incluindo a implementação das funções).

87 Exercício Crie uma lista encadeada que consegue armazenar qualquer tipo usando um template.

88 Operator Overloading Operadores como +, - , [] , == , etc, podem ter seu comportamento redefinido nas classes. Isso facilita a programação usando determinados tipos (ex: vetores 3d, strings).

89 Operator Overloading - Exemplo

90 Exercício Implemente uma classe String.
Faça overload do operador = que recebe um vetor de caracteres e armazena na string. Faça overload do operador += que recebe um vetor de caracteres e adiciona os caracteres ao final da string. Faça overload do operador == que recebe um ponteiro para caracteres constantes (const char*)

91 Lendo e Gravando Arquivos
Para ler ou gravar arquivos é necessário criar uma variável ponteiro para FILE. É preciso incluir o stdio.h. Para abrir um arquivo, usa-se a função fopen da seguinte forma FILE* fopen(path do arquivo, tipo de abertura)

92 Lendo e Gravando Arquivos
O tipo de abertura de fopen pode definir diferentes formas de abertura do arquivo: “r” : abre o arquivo para leitura. “w”: abre o arquivo para escrita. Se o arquivo já existir, seu conteúdo é destruído. “a”: abre o arquivo permitindo a escrita ao final do arquivo.

93 Lendo e Gravando Arquivos
Para a escrita de arquivos, usa-se a função fwrite que possui o seguinte protótipo: size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream)

94 Lendo e Gravando Arquivos
Para ler dados de arquivos, usa-se a função fread: size_t fread(void* buffer, size_t size, size_t count, FILE* stream)

95 Lendo e Gravando Arquivos
Para fechar arquivos usa-se fclose int fclose(FILE* stream)

96 Lendo e Gravando Arquivos

97 Exercício Crie um programa que salve as seguintes informações do jogador: Estágio atual Nome do personagem Nível do Personagem Posição x, y e z do personagem E depois crie um outro programa que lê esses dados e mostra na tela!

98 Multithreading Multithreading permite a criação de trechos de código que executam paralelamente. Se houver um único processador na máquina, as threads concorrem pelo processador. O sistema operacional dá um time slice para cada thread. Ao término do time slice, o SO interrompe a execução da thread e coloca outra thread para processar.

99 Multithreading Isso é útil em máquinas com múltiplos processadores por permitir processamento paralelo. É útil também quando existem operações que podem bloquear e não é desejável que o sistema bloqueie por essas operações (ex: bloqueio enquanto uma leitura de um arquivo grande é realizada).

100 Multithreading Para criar uma thread, utilizar a função _beginthread (incluir o process.h): uintptr_t _beginthread(função da thread, tamanho da pilha, ponteiro para a lista de parâmetros)

101 Multithreading

102 Multithreading Variáveis que são acessadas por diferentes threads correm o risco de se tornarem corrompidas. Imagine um programa onde existe uma lista de jogadores. Uma thread está adicionando um jogador na lista e outra thread está removendo outro jogador da lista. Pode acontecer da lista acabar com um dos jogadores apontando para lixo e até coisas piores. Por isso é necessário usar funções que permite acesso à regiões críticas.

103 Multithreading Para proteger regiões acessadas por múltiplas threads utilize CRITICAL_SECTION (inclua windows.h) Use a API InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection e DeleteCriticalSection para usar regiões críticas.

104 Multithreading

105 Exercício Crie uma aplicação com 2 threads adicionais.
1 thread incrementa um contador e o imprime na tela 1 thread decrementa esse mesmo contador e o imprime na tela. Cada thread repete esse processo 50 vezes. A main thread fica em espera ocupada esperando as threads terminarem. No final ela imprime THE END. Faça testes usando critical sections e sem usar critical sections.

106 Tópicos para Computação Gráfica
Aplicações Win32 Pipeline gráfico do Direct3D e Dispositivo Direct3D Buffer de Vértices e Índices Effect Files, Vertex Shaders e Pixel Shaders com Cg Renderizando triângulos Matrizes e Transformações Sistemas de Coordenadas e o Processo de Visualização Renderizando triângulos com perspectiva Renderizando triângulos texturizados Produto Escalar Iluminação Renderizando Objetos com Iluminação por pixel Exportando e carregando modelos de arquivos .X (e renderizando!) Animação usando interpolação entre Key Frames Alpha Blending Volumes de Sombras Bump Mapping Grafos de Cena Culling em cenários usando Octrees

107 Programando aplicações Win32
Para criar uma aplicação Win32 é preciso incluir o windows.h O processamento em aplicações Win32 começa na WinMain.

108 Programando aplicações Win32
hinstance é um handle. Um handle é simplesmente um número que identifica alguma coisa. hinstance é um identificador da aplicação. Esse parâmetro é necessário para alguma funções. hprevinstance é um parâmetro não mais utilizado e é definido como 0. cmdline é a linha de comando usada para rodar o programa. cmdshow indica como o programa deveria ser inicialmente mostrado.

109 Programando aplicações Win32
Para criar a janela da aplicação é preciso instanciar e preencher a struct WNDCLASS que possui a seguinte cara.

110 Programando aplicações Win32

111 Programando aplicações Win32
style: define propriedades da janela. CS_VREDRAW fazem com que a janela seja redesenhada caso haja uma mudança na altura da janela. lpfnWndProc: recebe a função que trata os eventos enviados para a janela de sua aplicação. cbClsExtra: número de bytes extras para alocar para a windows class struct. cbWndExtra: número de bytes extras além das instância da janela. hInstance: handle da aplicação. hIcon: handle do ícone usado. hCursor: handle do cursor usado. hbhBackground: handle do pincel usado para pintar o background da janela. lpszMenuName: um nome que identifica o recurso de menu usado na aplicação. lpszClassName: especifica o nome dessa classe de janela.

112 Programando aplicações Win32
Após definir as características da classe de janela, é preciso registrá-la.

113 Programando aplicações Win32
Após o registro da classe de janela, é preciso criar a janela e mostrá-la.

114 Programando aplicações Win32
Após criar a janela é necessário entrar no loop de captura e envio de mensagens. Nesse loop também são colocadas funções para executar o jogo.

115 Programando aplicações Win32
A última parte que falta é a parte da função de tratamento de eventos

116 Exercício Junte todo o código mostrado e crie uma aplicação win32.
Implemente a aplicação para mostrar a posição x e y do mouse quando o botão esquerdo é pressionado. Quando a tecla pra cima é pressionada mostrar também.

117 Dispositivo Direct3D API Gráfica do DirectX
Permite acesso à placas de aceleração gráfica de maneira única e independente do dispositivo.

118 Pipeline Gráfico do Direct3D

119 Pipeline Gráfico do Direct3D
Vertex Data: Dados geométricos dos objetos 3D do cenário. Por exemplo, as posições dos vértices do objeto 3D. Fixed Function Pipeline: Uma sequência de processamento fixa do Vertex Data. As alterações nessa sequência de processamento são pouco flexíveis. Programable Pipeline: Se usado, permite a implementação de um programa que processa os vértices. Permite muita mais flexibilidade de processamento dos vértices.

120 Pipeline Gráfico do Direct3D
Clipping: Esse estágio recorta todas as partes dos triângulos que estão para fora da tela.

121 Pipeline Gráfico do Direct3D
Back Face Culling: Elimina os triângulos que estão de “costas para a câmera”. Esse estágio elimina triângulos não visíveis em objetos fechados.

122 Pipeline Gráfico do Direct3D
Rasterization: Nesse estágio, os triângulos são desenhados.

123 Pipeline Gráfico do Direct3D
Pixel Shader: Se usado, permite ao programador criar um processamento que executa a cada pixel desenhado. Permite grande flexilidade sobre a forma como os pixels são renderizados. Texture Sampler: Esse estágio, se usado, captura amostras das textura para serem usadas no Pixel Shader.

124 Pipeline Gráfico do Direct3D
Alpha Test: Esse estágio permite configurar a aceitação de um pixel de acordo com o seu alpha. Depth Test: Esse estágio permite configurar a aceitação de um pixel de acordo com a sua profundidade. Stencil Test: Esse estágio permite configurar a aceitação de um pixel de acordo com o Stencil Buffer.

125 Pipeline Gráfico do Direct3D
Fogging: Esse estágio permite configurar fumaça na cena a medida que os objetos se distanciam. Alpha Blending: Esse estágio permite que o pixel sendo renderizado seja combinado com o pixel já desenhado no buffer de cor que será apresentado na tela.

126 Pipeline Gráfico do Direct3D
A geometria dos modelos e cenários passa pelo pipeline do Direct3D e ao final é gerada uma imagem 2D do objeto 3D, que é mostrada na tela.

127 Integração de Sistemas

128 Sistemas de Coordenadas
Direct3D usa o sistema de coordenadas da mão esquerda.

129 O objeto Direct3D É o primeiro objeto a ser criado e o último a ser liberado.

130 O dispositivo Direct3D Objeto principal de renderização.
É com esse objeto que passamos a geometria de modelos para a placa gráfica.

131 Criando um dispositivo Direct3D
Preencher D3DPRESENT_PARAMETERS

132 Criando um dispositivo Direct3D
BackBufferWidth, BackBufferHeight: Largura e altura do Backbuffer. BackBufferFormat: Formato do backbuffer. BackBufferCount: Número de Backbuffers. MultiSampleType e MultiSampleQuality: Parâmetros usados para antialiasing. SwapEffect: Maneira como o backbuffer vira o front buffer. hDeviceWindow: handle da janela Windowed: Se a aplicação vai rodar em janela ou full screen. EnableAutoDepthStencil: Permissão se o Direct3D pode gerenciar o seu buffer de depth e stencil. AutoDepthStencilFormat: O formato do buffer de depth e stencil. Flags: ... FullScreen_RefreshRateInHz: A taxa com que o monitor é atualizado. PresentationInterval: A taxa com que o backbuffer pode ser apresentado na tela.

133 Criando um dispositivo Direct3D
Criar o dispositivo usando a função CreateDevice do objeto Direct3D.

134 Criando um dispositivo Direct3D
Adapter: número que identifica a placa gráfica usada. DeviceType: O tipo de dispositivo a ser usado (Ex. HAL ou Ref). hFocusWindow: handle para a janela BehaviorFlags: flags pPresentationParameters: a estrutura PRESENT_PARAMETERS preenchida anteriormente. ppReturnedDeviceInterface: retorna o dispositivo Direct3D criado.

135 Criando um dispositivo

136 Exercício Crie um dispositivo Direct3D em 800x600 fullscreen.
Experimente com outros tamanhos. Crie um dispositivo para versão Windowed. Para isso coloque Windowed = TRUE, e o fullscreen refresh rate = 0.

137 Cores As cores no Direct3D são definidas geralmente pela combinação de vermelho, verde e azul. Um componente chamado alpha também é usado eventualmente e representa o grau de transparência da cor.

138 Cores Em Direct3D, cores de vértices são geralmente definidas como números hexadecimais com o seguinte formato: 0xalpharedgreenblue 0x00ff0000 = vermelho puro 0x0000ff00 = verde puro 0x000000ff = azul puro 0x = preto 0x00ffffff = branco

139 Limpando Buffers Toda vez que se deseja renderizar um frame, é necessário limpar os buffers de cor e profundidade. Caso o buffer de cor não seja limpo, partes das cenas de renderizações passadas serão mostradas na tela, já que os backbuffers são reutilizados. Caso um jogo no entando sempre desenha na tela toda, não é necessário limpar esse buffer. O buffer de profundidade é usado para desenhar corretamente os objetos 3D com relação à distância para a câmera. Basicamente esse buffer garante que objetos 3D que estão na frente de outros objetos, são renderizados corretamente na frente desses objetos. Esse buffer sempre precisa ser limpo a cada nova renderização.

140 Limpando Buffers Para limpar os buffers de cor e profundidade é necessário usar a função Clear do dispositivo Direct3D.

141 Limpando Buffers Count: Número de retângulos em pRects.
pRects: Buffer de retângulos que especificam as regiões que irão sofrer limpeza. Flags: flags que especificam os buffers que serão limpos. Color: cor usada para limpar o buffer de cor. Z: valor de profundidade usado para limpar o buffer de profundidade. Stencil: valor de stencil usado para limpar o buffer de stencil.

142 Limpando Buffers

143 Apresentando Back Buffers
Toda a renderização por default é feita no backbuffer. Após a renderização é necessário apresentar o backbuffer, fazendo seus dados serem copiados para o front buffer, que é o buffer sendo mostrado na tela.

144 Apresentando Back Buffers
Para apresentar o back buffer deve ser usada a função Present do dispositivo Direct3D.

145 Apresentando Back Buffers
pSourceRect: Um retângulo que define a parte que será apresentada do Back Buffer. pDestRect: Um retângulo que define o local onde será apresentado o back buffer. hDestWindowOverride: handle da janela onde será apresentado o back buffer. pDirtyRegion: Define a região que precisa ser atualizada.

146 Apresentando Back Buffers

147 Exercício Crie uma aplicação que limpa o buffer de cor para a cor vermelha (0x00ff0000).

148 Objetos 3D Um objeto 3D pode possuir diversas informações que são usadas na sua renderização.

149 Objetos 3D

150 Objetos 3D Objetos 3D podem usar texturas(imagens) para dar detalhe à seus triângulos. Coordenadas de textura podem ser usadas para aplicar imagens à um objeto 3D.

151 Objetos 3D Normais também podem ser usadas para cálculos de iluminação por exemplo.

152 Objetos 3D (Resumo) Para desenhar objetos 3D nós podemos usar:
Posições dos vértices Índices das faces Coordenadas de textura Normais dos vértices

153 Buffer de Vértices e Índices
Objetos 3D são renderizados passando sua informação geométrica para a placa gráfica. A informação geométrica dos objetos é armazenada em VertexBuffers e IndexBuffers. Os vértices dos objetos podem possuir variações em seu conteúdo. Nos VertexBuffers ficam armazenadas informações tipo posição x,y,z, coordenadas de textura u, v, normal, etc.

154 Criando um Vertex Buffer
Para isso é necessário usar a função CreateVertexBuffer do dispositivo Direct3D.

155 Criando um Vertex Buffer
Length: tamanho do vertex buffer em bytes. Usage: Flags que indicam usos especiais. FVF: Flags que indicam o conteúdo do vertex buffer. Pool: Memória onde esse buffer ficará armazenado (Memória de Vídeo, AGP ou RAM normal). ppVertexBuffer: Retorna o vertex buffer. pSharedHandle: colocar NULL. (bacalhau M$)

156 Criando um Vertex Buffer

157 Carregando Dados no Vertex Buffer
Para carregar dados em um vertex buffer deve-se usar a função Lock do vertex buffer. Após carregar os dados, deve-se usar a função Unlock.

158 Carregando dados no Vertex Buffer
OffsetToLock: Distância do início do vertex buffer onde o carregamento começará. SizeToLock: Tamanho da região no vertex buffer onde os dados serão carregados. ppbData: Ponteiro retornado que aponta para o conteúdo do vertex buffer. Flags: ...

159 Carregando dados no Vertex Buffer

160 Vertex Buffers Vertex buffers podem armazenar diferentes informações sobre um vértice. Ex: x,y,z Coordenadas de textura u, v Normal Cor do vértice

161 Exercício Crie um vertex buffer e carregue dados x,y,z de um triângulo. Leia esses dados em outro buffer usando o mesmo vertex buffer.

162 O processo de renderização de primitivas

163 Processo de renderização de primitivas
As informações de renderização (x,y,z, u, v, ...) do objeto são armazenadas em vertex buffers. Esses buffers são colocados em uso através da função SetStreamSource do dispositivo Direct3D. A função DrawPrimitive faz as informações dos buffers serem carregadas juntando a informação completa de um vértice. Quem define como os vertex buffers são combinados formando um vértice é a declaração do vértice ...

164 Colocando vertex buffers em uso
Para colocar em uso um vertex buffer, deve-se usar a função SetStreamSource do dispositivo Direct3D.

165 Colocando vertex buffers em uso
StreamNumber: define o número do vertex buffer, começa em 0. pStreamData: o vertex buffer OffsetInBytes: a distância em bytes no buffer onde começa a informação dos vértices. Stride: A distância entre cada componente do vertex buffer.

166 Colocando vertex buffers em uso

167 Exercício Coloque o vertex buffer criado no exercício anterior para ser usado como o vertex buffer 0. Crie um vertex buffer para armazenar uma cor para cada um dos vértices do triângulo. Use D3DCOLOR. Colocar esse vertex buffer com as cores como o vertex buffer 1.

168 Declarações de Vértices
Os vertex buffers podem conter diferentes informações sobre o vértice. A declaração do vértice informa ao Direct3D como ele deve processar os vertex buffers usados para adquirir a informação de um vértice. Declarações de vértices dizem coisas do tipo: “O vertex buffer 0 tem a informação x,y,z do objeto, já o vertex buffer 1 tem a informação u,v do objeto”.

169 Criando uma declaração de vértices
Uma declaração de vértices é feita através da criação de um buffer onde cada elemento é um D3DVERTEXELEMENT9.

170 Criando uma declaração de vértices
Stream: o número do vertex buffer. Offset: a distância no vertex buffer onde começa a informação. Type: O tipo de dado armazenado no buffer. Method: Define como o tesselator vai operar na informação do vertex buffer. Usage: Define o objetivo do uso do vertex buffer. UsageIndex: Dado adicional ao objetivo do vertex buffer.

171 Criando uma declaração de vértices

172 Criando uma declaração de vértices
Após criar o buffer de D3DVERTEXELEMENT9, uma declaração é criada chamando a função CreateVertexDeclaration do dispositivo Direct3D.

173 Criando uma declaração de vértices

174 Usando uma declaração de vértices
Para usar uma declaração de vértices usa-se a função SetVertexDeclaration do dispositivo Direct3D.

175 Exercício Crie a vertex declaration para usar os buffers do exercício anterior.

176 Effect Files Effect files são arquivos que estabelecem uma configuração do pipeline gráfico para renderizar alguma coisa. Eles armazenam o vertex e o pixel shader.

177 Composição de um effect file.

178 Vertex Shader É responsável pelo processamento dos vértices.
É um programa que executa para cada vértice passado para o pipeline. Recebe como parâmetro as informações dadas pelos vertex buffers. O pipeline espera obrigatoriamente pela posição do vértice. Opcionalmente, é possível enviar outras informações como coordenadas de textura e cores. Essas informações serão interpoladas por toda a face do triângulo sendo desenhado.

179 Vertex Shader Ao final do processamento do Vertex Shader, o vértice é passado para o estágio de clipping. O vértice estará na tela, se estiver entre as coordenadas (-1, -1, 0, 1) e (1, 1, 1, 1). Se o vértice estiver fora do volume mostrado na imagem, ele fará com que o triângulo a que ele pertence seja clipado.

180 Vertex Shader

181 Vertex Shader Dados de input: Dados de output:
POSITION: define que a variável receberá a informação de posição dos vertex buffers. TEXCOORD0: define que a variável receberá a informação de coordenadas de textura 0 do vertex buffers. Dados de output: POSITION: parâmetro de retorno obrigatório. Recebe uma posição no espaço de clip homogêneo. TEXCOORD0: recebe uma coordenada de textura que será interpolada linearmente pela face do triângulo.

182 Pixel Shader O parâmetro de retorno obrigatório do pixel shader é a cor final do pixel.

183 Pixel Shader Parâmetros de input: Parâmetros de output:
TEXCOORD0: coordenada de textura interpolada.[ Parâmetros de output: COLOR: a cor final do pixel. (no exemplo estamos colocando a cor branca)

184 Usando Effect files no Direct3D
Para criar um efeito no Direct3D usa-se a função D3DXCreateEffectFromFile.

185 Usando Effect files no Direct3D
pDevice: o dispositivo Direct3D pSrcFile: path do effect file pDefine: permite a definição de macros do arquivo de efeitos. pInclude: permite a definição de includes no arquivo de efeitos. Flags: ... pPool: Usado para permitir o uso compartilhado de parâmetros em múltiplos efeitos. ppEffect: retorna o objeto efeito. ppCompilationErrors: retorna uma lista de erros de compilação do efeito.

186 Usando Effect files no Direct3D

187 Renderizando Triângulos
Para renderizar triângulos é preciso usar a função DrawPrimitive do dispositivo Direct3D.

188 Renderizando Triângulos
PrimitiveType: Define o tipo de primitiva para renderizar. (lista de ponto, de linhas, de triangulos, etc ...) StartVertex: índice do primeiro vértice a ser carregado para renderização. PrimitiveCount: número de primitivas a serem renderizadas.

189 Renderizando Triângulos
Antes de qualquer DrawPrimitive deve ser chamada a função BeginScene do dispositivo Direct3D. Depois de todas as chamadas de DrawPrimitive deve ser chamada a EndScene do dispositivo Direct3D.

190 Resumo Básico de Renderização de Objetos 3D
Definir os vertex buffers usados pelo objeto 3d. Definir a declaração de vértice do objeto 3d. Com isso o Direct3D consegue entender a informação que existe dentro dos buffers. Renderizar o objeto 3d com a função DrawPrimitive.

191 Renderizando triângulos

192 Exercício Use os vertex buffers e o vertex declaration dos exercícios anteriores e renderize um triângulo. Para isso é necessário alterar o vertex shader para receber um float4 color : COLOR0 e retornar também um COLOR0. Altere também o pixel shader para receber esse COLOR0 e colocá-lo na tela. Implemente um código que mexe o triângulo em x e y baseado nas teclas recebidas.

193 Matrizes e Transformações
Matrizes são uma parte fundamental na computação gráfica. Elas permitem: Posicionar objetos 3D no mundo. Posicionar câmeras no mundo. Projetar o cenário 3D em um plano permitindo a visualização em monitores.

194 O que são matrizes? São um conjunto de elementos organizados em linhas e colunas

195 Operações Básicas

196 Multiplicação Não é Comutativa

197 Vetores Podem ser representados por matrizes 1xN ou Nx1.
Consistem em uma entidade matemática que possui direção e tamanho. Representado por uma linha com uma ponta de flecha.

198 Transformações Matrizes são usadas para realizar transformações em vetores.

199 Transformações São operações realizadas em pontos que permitem até alterar o sistema de coordenadas do ponto. (Ex: um ponto 3D vira um ponto 2D).

200 Transformações

201 Transformações Simples

202 Transformações Simples
Matriz Identidade Matriz de Rotação 2D Matriz de Escala Uniforme Como Implementar Translação?

203 Coordenadas Homogêneas
Coordenadas homogêneas permitem que translações sejam realizadas com multiplicações de matrizes. As coordenadas homogêneas de um ponto de n dimensões possuem n+1 dimensões. A coordenada adicional é um escalar que multiplica todas as outras coordenadas. (x,y,z) = (wx, wy, wz, w)

204 Coordenadas Homogêneas
Representando um ponto 2D em coordenadas homogêneas: (x, y) = (x, y, 1) ou (2x, 2y, 2) Para achar um ponto 2D representado em coordenadas homogêneas: (2x, 2y, 2) = (2x/2, 2y/2, 2/2) = (x, y, 1)

205 Adicionando Translação
Para realizar a translação, basta usar o escalar com valor 1.

206 Sistemas de Coordenadas e Processo de Visualização
O pipeline gráfico pega a geometria dos objetos em 3D e cria uma imagem em 2D a partir dela. Para gerar uma imagem 2D de um cenário 3D visto de um determinado ângulo é necessário fazer com que os vértices de um objeto passem por vários sistemas de coordenadas.

207 Sistemas de coordenadas e processo de visualização

208 Sistema de coordenadas locais ou do objeto ou do modelo
Quando um objeto está sendo modelado, seus vértices em 3D são especificados com relação a alguma origem. Geralmente essa origem fica em alguma região próxima ao objeto ou no centro do objeto.

209 Sistema de coordenadas do mundo
Os vértices do objeto no espaço precisam ser transformados para que o objeto possa ser posicionado no mundo. Essa transformação é quem permite posicionar um objeto em cima de uma mesa, ou dois orcs lado a lado. Isso se dá através da matriz de mundo. Após a transformação os vértices estão no espaço do mundo. Todas as transformações são feitas baseadas em uma mesma origem de mundo, por isso elas permitem posicionar os objetos no mundo. A transformação que transforma uma posição do espaço do objeto para o espaço do mundo é chamada de transformação de mundo.

210 Sistema de coordenadas de olho ou visão
Após transformar os vértices para o espaço de mundo, eles devem ser transformado para o espaço do olho. Isso se dá com a matriz de visão. O espaço do olho transforma o mundo de acordo com a posição e orientação da câmera. Ao final da transformação a câmera está na posição 0,0,0 olhando para +z. O mundo é transformado de maneira que a câmera continue a ver o que ela deveria ver em sua posição original. Na imagem, é mostrado como a câmera é posicionada no OpenGL. Em Direct3D a câmera aponta para +z. Não existe um objeto câmera que é transformado. A posição e orientação da câmera são utilizadas apenas para transformar o mundo de maneira que a câmera fique na posição 0,0,0 e olhando para +z. Essa transformação faz esse reposicionamento do mundo para permitir que a próxima etapa de visualização aconteça de maneira rápida e eficiente.

211 Sistema de coordenadas de clipping homogêneo
Após os vértices serem reposicionados levando em consideração a câmera, é hora de projetá-los na tela. Os vértices são transformados pela matriz de projeção. Após a transformação eles estão num espaço chamado espaço de clipping homogêneo que permite o pipeline gráfico executar o algoritmo de clipping que remove as partes das primitivas que não são visíveis.

212 Sistema de coordenadas normalizadas de dispositivo
Após o clipping os vértices têm seu x,y e z divididos por w, o que os deixa no espaço normalizado de dispositivo.

213 Sistemas de coordenadas da tela
A última transformação é a transformação de viewport. Essa transformação é quem coloca os vértices na posição correta na tela e permite que os triângulos sejam rasterizados.

214 Transformações principais
As principais transformações que nós trabalharemos serão as transformações de mundo, de visão e de projeção.

215 Matrizes de Mundo Essas matrizes variam dependendo da forma como se deseja posicionar os objetos no mundo.

216 Matrizes de Visão Essa matriz que coloca a posição da câmera no processamento gráfico.

217 Matrizes de Projeção A matriz de projeção vai definir a forma como os vértices serão projetados.

218 Renderizando triângulos com perspectiva
Após as matrizes de mundo, visão e projeção serem definidas, é preciso multiplicá-las na ordem World * View * Proj e depois passar a matriz resultando para o vertex shader. No vertex shader, a matriz tem que ser usada para multiplicar as posições dos vértices.

219 Renderizando triângulos com perspectiva

220 Renderizando triângulos com perspectiva

221 Exercício Renderize 3 triângulos, um atrás do outro. Utilize 1 único vertex buffer para os 3 triângulos. (Dica: Use uma matriz de mundo para cada triângulo). Posicione a câmera em um ângulo que permita ver os 3 triângulos Trate o input do teclado e faça a câmera se mover.

222 Exercício Anime os 3 triângulos da seguinte forma:
1 triângulo rotaciona no eixo y fixo na posição 0, 0, 0 1 triângulo rotaciona no eixo y fixo na posição 10, 0, 0. 1 triângulo rotaciona no eixo y na posição 20, 0, 0

223 Renderizando triângulos com textura
Texturas são um dos principais elementos que dão realismo à um cenário 3D. Texturas são imagens que são colocadas nos modelos 3d que dão detalhes aos modelos.

224 Renderizando Triângulos com texturas
Para criar texturas, nós usaremos a função D3DXCreateTextureFromFile.

225 Renderizando Triângulos com texturas
Na hora de renderizar, usamos a função SetTexture do efeito.

226 Renderizando triângulos com textura
Temos que definir as coordenadas de textura, carregar o vertex buffer e definir o vertex declaration.

227 Renderizando triângulos com textura

228 Exercício Coloque os triângulos do exercício anterior usando texturas.
Use texturas diferentes para cada triângulo.

229 Produto Escalar Vamos achar o tamanho de b, usando os lados a e c e o
ângulo entre a e c? a^2 = h^2 + c1^ b^2 = h^2 + c2^2 ... h^2 = a^2 – c1^2 b^2 = a^2 – c1^2 + c2^ c = c1 + c2 b^2 = a^2 – c1^2 + (c-c1)^2 b^2 = a^2 – c1^2 + c^2 – 2cc1 + c1^2 ... cos theta = c1/a b^2 = a^2 + c^2 – 2cc1 ... b^2 = a^2 + c^2 – 2ca cos theta

230 Lei dos Cosenos A fórmula achada é a lei dos cosenos.
Ela permite achar o tamanho de b usando os lados a e c e o ângulo entre eles. b^2 = a^2 + c^2 – 2*a*c*cos(ab)

231 Antes uma informação ... |a| = tamanho do vetor a
|a| = raiz(ax^2 + ay^2 + az^2)

232 Usando vetores |b-a|^2 = |a|^2 + |b|^2 – 2*|a|*|b|*cos (ab)
(bx-ax)^2 + (by-ay)^2 + (bz-az)^2 – ax^2 – ay^2 – az^2 – bx^2 – by^2 – bz^2 bx^2 – 2bxax + ax2 + by^2 – 2byay + ay^2 + bz^2 – 2bzaz + az^ 2bxax - 2byay – 2bzaz = -2*|a|*|b|*cos(ab) axbx + ayby + azbz = |a|*|b|*cos(ab) Produto escalar: a * b = axbx + ayby + azbz. Ou seja, o produto escalar entre 2 vetores calcula uma informação de ângulo entre os vetores. Se os 2 vetores tiverem tamanho 1, o produto escalar dá o coseno entre os vetores.

233 Projetando um vetor em outro
O que acontece quando um dos vetores possui tamanho 1? a * b = |a| * cos (ab) Nesse caso a * b dá o tamanho do vetor a projetado no vetor b. Então para achar o vetor projetado de a em b basta achar o a * b e multiplicar pelo vetor b .... a * b = t .... proj_a = multiplicar(b, t)

234 Exercício Crie uma classe Vector3d com x,y e z como floats.
Crie a função Length que acha o tamanho do vetor Crie a função Normalize que normaliza esse vetor (Basta dividir os componentes pelo tamanho do vetor) Crie a função Dot que acha o produto escalar entre 2 vetores Ache o vetor projetado de um vetor em outro normalizado.

235 Produto Vetorial Outra ferramenta extremamente importante para programadores gráficos é o produto vetorial. O produto vetorial entre dois vetores não paralelos é outro vetor perpendicular aos dois vetores. O produto vetorial é escrito como: a x b = c Onde a, b e c são vetores e c é perpendicular aos vetores a e b.

236 Como calcular o produto vetorial?
Abaixo a * b significa o produto escalar entre os vetores a e b. a * (a x b) = 0 b * (a x b) = 0 Ou seja: axcx + aycy + azcz = 0 bxcx + bycy + bzcz = 0

237 Calculando o produto vetorial
Multiplicando a primeira linha por bx/ax: bxcx + bxaycy/ax + bxazcz/ax = 0 Subtraindo a linha acima da segunda linha: bxcx + bycy + bzcz – bxcx – bxaycy/ax – bxazcz/ax = 0 bycy + bzcz = bxaycy/ax + bxazcz/ax axbycy + axbzcz = bxaycy + bxazcz axbycy – bxaycy = bxazcz – axbzcz (axby – bxay)cy = (bxaz – axbz)cz

238 Calculando o produto vetorial
Multiplicando a primeira linha por by/ay: byaxcx/ay + bycy + byazcz/ay = 0 Subtraindo da segunda linha: bxcx + bycy + bzcz – byaxcx/ay – bycy – byazcz/ay = 0 bxcx + bzcz = byaxcx/ay + byazcz/ay aybxcx + aybzcz = byaxcx + byazcz aybxcx – byaxcx = byazcz – aybzcz (aybx – byax)cx = (byaz – aybz)cz

239 Logo... (axby – bxay)cy = (bxaz – axbz)cz
(aybx – byax)cx = (byaz – aybz)cz Vamos alterar a segunda linha: -1*(axby – bxay)cx = (byaz – aybz)cz (axby – bxay)cx = (aybz – byaz)cz

240 Logo .... (parte 2) (axby – bxay)cy = (bxaz – axbz)cz
(axby – bxay)cx = (aybz – byaz)cz Queremos encontrar cx, cy e cz que são nossas incógnitas. Que valores cx, cy e cz possuem? Ele podem possuir infinitos valores ... Porém se você selecionar um valor qualquer ... Coisas ruins podem acontecer ... como divisão por zero ... ouch.

241 Logo .... (parte 3) (axby – bxay)cy = (bxaz – axbz)cz
(axby – bxay)cx = (aybz – byaz)cz E se selecionarmos cz = axby – bxay? Nesse caso: cy = bxaz – axbz cx = aybz – byaz Para achar as incógnitas não precisa de nenhuma divisão ... o que é bom. Portanto, o produto vetorial entre vetores a x b: a x b = c = (aybz – byaz, bxaz – axbz, axby – bxay)

242 Exercício Adicione o calculo vetorial no último exercício e faça testes

243 Iluminação Iluminação permite a geração de cenas mais interessantes e realistas. Existem diversas formas de calcular a iluminação de uma cena. Nós utilizaremos um modelo de iluminação semelhante ao Direct3D e OpenGL, porém usaremos o cálculo de iluminação por pixel.

244 Iluminação A cor de um pixel é calculada da seguinte forma:
Cor = Contribuição Emissiva + Contribuição Ambiente + Contribuição Diffusa + Contribuição Especular.

245 Contribuição Emissiva
Essa contribuição representa a luz emitida pelo próprio objeto.

246 Contribuição Ambiente
Consiste no quanto o objeto reflete a iluminação espalhada pelo ambiente. A contribuição ambiente é calculada da seguinte forma: Material Ambiente * Iluminação Ambiente Material Ambiente representa o quanto da iluminação ambiente o objeto reflete.

247 Contribuição Difusa Essa contribuição representa o quanto o objeto reflete para todos os lados a iluminação direta das luzes. A contribuição difusa é calculada da seguinte forma: Max((N * L),0) * Material Diffuso * Iluminação Diffusa N = Normal normalizada do ponto em que está sendo calculada a iluminação. L = Vetor para luz normalizado. Material Diffuso representa o quanto da iluminação diffusa o objeto reflete.

248 Contribuição Especular
A contribuição especular representa o quanto de luz direta é refletida na direção principal de reflexão de uma luz. A contribuição especular é calculada da seguinte forma: Max((H * N),0)^shininess * Material Especular * Iluminação Especular H = vetor da luz + vetor do olho shininess define o quão brilhoso é o objeto. Material Especular define o quanto o objeto reflete a iluminação especular.

249 Renderizando objetos com iluminação por pixel

250 Renderizando objetos com iluminação por pixel

251 Transformando Normais
Para transformar normais é necessário multiplicá-las pela inversa da transposta da matriz de mundo. Ou seja: n’ = n * (world^t)^-1

252 Compreendendo Transformações de Normais*
Primeiro: Estudar equação do plano (ver parte do ppt de física) [nx ny nz –nP0] * [px py pz 1]^t = 0 q * p^t = 0 q * M * (p * R)^t = 0 q * M * R^t * p^t = 0 M * R^t = Identidade M = (R^t)^-1

253 Exercício Implemente o exemplo anterior usando iluminação por pixel.

254 Exportando, Carregando e Renderizando modelos .X
Criar objetos 3D no código é relativamente extraordinariamente complexo de se fazer quando algo sofisticado é desejado. A maneira correta de se criar cenários e objetos 3d é modelando-os em programas como o 3D Studio Max. O problema é que o 3D Studio Max pode exportar em qualquer formato desejado (Se você implementar plugins). Logo precisamos definir um formato com o qual trabalharemos.

255 Arquivo .X (O modelo, não a série)
Arquivos .X são interessantes porque a micro$oft já se deu o trabalho de implementar um carregador. Nós precisamos no entanto achar um plugin para o 3ds que exporte arquivos .X. (Procure no google por Panda DirectX Exporter). Nós iremos carregar o modelo usando a API do DirectX e depois extrairemos a informação geométrica, e com isso, iremos renderizar na tela modelos realmente interessantes...

256 Carregando Arquivos .X (Para pipeline fixo)

257 Renderizando Malhas DirectX (Usando pipeline fixo)

258 Exercício Exporte um modelo no 3D Studio Max e carregue-o e renderize-o usando pipeline fixo. (Para casa) Exporte um arquivo .ASE e tente escrever um parser dele.

259 Preparando malhas para uso em shaders

260 Preparando malhas para uso em shaders
Após carregar a malha, nós criamos uma nova malha com um formato compatível com o nosso vertex shader. Nesse formato apenas um vertex buffer deve ser usado. (tentei com mais de 1 e não deu).

261 Renderizando a malha com shaders

262 Exercício Renderize a malha usando shaders.

263 Animação utilizando interpolação entre keyframes
Nesse tipo de animação, um personagem é modelado em diferentes posições (como as imagens de desenhos animados) e para renderizar uma posição intermediária entre 2 key frames é feita uma interpolação linear entre 2 frames: Frame1.pos * (1-t) + Frame2.pos * t t define a posição intermediária da animação. 0 representa o frame 1 e 1 representa o frame 2. Um t = 0.5 representa uma posição no meio do caminho entre os frames 1 e 2.

264 Animação utilizando interpolação linear entre keyframes
Para conseguirmos fazer a interpolação de maneira rápida (na GPU) precisamos enviar as posições dos dois keyframes para o vertex shader interpolar. Para isso usaremos coordenadas de texturas.

265 Animação usando interpolação entre keyframes

266 Animação usando interpolação entre keyframes

267 Animação usando interpolação entre keyframes

268 Animação usando interpolação entre keyframes

269 Exercício Implemente um exemplo que anima um triângulo.

270 Alpha Blending Usado para renderizar objetos com transparência.
Quando o alpha blending está habilitado, a cor final de um pixel após renderizar uma primitiva é calculada da seguinte maneira: Final Color = (New Color * SourceBlendFactor) + (Pixel Color * DestBlendFactor)

271 Alpha Blending O resultado do pixel shader é uma cor.
Essa cor possui os componentes rgba. Nós trabalhos com os componentes rgb até agora para dar a cor aos objetos. O componente alpha permite definir um nível de transparência quando alpha blending está habilitado. Geralmente 1 representará um pixel totalmente opaco, e 0 um pixel totalmente transparente. Para definir o componente alpha da cor gerada pelo pixel shader use:

272 Alpha Blending Para habilitar alpha blending:

273 Alpha Blending Para definir os fatores de blending:

274 Exercício Renderize o triângulo da cena de forma transparente
(enigma) Se o triângulo transparente for renderizado antes do chão e do objeto .x ele terá sua cor calculada com o fundo preto, como fazer a cena aparecer transparente atrás do triângulo? (dica) Utilize um único float para definir o alpha do triângulo.

275 Volumes de sombras Sombras são muito importantes para posicionar objetos em um ambiente 3d. Através das sombras é possível saber a relação espacial entre os objetos. A técnica de volume de sombras gera sombras através da criação de um volume de sombras gerado através da malha do objeto e o stencil buffer.

276 Volume de sombras O volume de sombra é um objeto 3d que representa a região em sombra de outro objeto.

277 Stencil Buffer É necessário usar o stencil buffer para a técnica de volumes de sombra. O stencil buffer é um buffer que permite um controle à nível de pixel sobre o que é renderizado na tela. Quando o stencil buffer está habilitado, um pixel só é renderizado quando ele passa na seguinte operação: (StencilRef & StencilMask) CompFunc (StencilBufferValue & StencilMask) Todos esses elementos são customizáveis e o StencilBufferValue é o valor do stencil buffer.

278 O Algoritmo Limpar o Stencil Buffer para 0
Gerar o volume de sombras através da malha do objeto e da posição da luz. Renderizar o volume de sombra sem back face culling e sem z-writing. Por toda a região de todas as faces de frente do volume de sombra, adicionar 1 ao stencil buffer. Por toda a região de todas as faces de trás do volume de sombras, remover 1 do stencil buffer. Se um determinado pixel falhar em z, não alterar o stencil buffer. Renderizar um Quad ocupando toda a tela, naquelas regiões do stencil que forem diferentes de 0, escurecer.

279 O Algoritmo Limpar o Stencil Buffer para 0

280 O Algoritmo Gerar o volume de sombras através da malha e da posição da luz. Para isso, usaremos uma malha especial.

281 O Algoritmo Durante a criação do vertex buffer do volume de sombras para cada aresta do objeto definir triângulos da seguinte forma: (triangulos da aresta v1v2) = {v1,~v2, ~v1, v1, v2, ~v2} . ~v1 é igual a v1 só que com o w = 0. ~v2 tem a mesma relação com v2.

282 O Algoritmo Durante a renderização do volume de sombras, todos os vértices com w = 0 são afastados da luz uma certa distância e depois transformados. Os vértices com w != 0 são transformados normalmente.

283 O Algoritmo Para renderizar o volume sem back face culling e sem z-writing.

284 O Algoritmo Para permitir que o stencil buffer faça uma operação com as faces da frente e outra operação com as faces de trás.

285 O Algoritmo Para definir a adição no stencil buffer para todas as faces de frente e subtração para todas as faces de trás.

286 O Algoritmo Use alpha blending para renderizar volumes transparentes!

287 O Algoritmo Nesse momento o stencil buffer só possui valores diferentes de zero nas regiões de sombra. Para renderizar apenas nessas regiões:

288 Exercício Coloque o triângulo renderizando com um volume de sombras.

289 Bump Mapping Bump mapping é uma técnica que aumenta o realismo das cenas ao dar a aparência de mais detalhe em malhas geométricas. Para implementar a técnica, deve ser usada uma textura especial que define como é a superfície de um determinado triângulo de uma malha. Essa informação de superfície é usada na hora de calcular iluminação, e com isso o objeto parece ter muito mais detalhe do que ele possui.

290 Bump Mapping

291 Bump Mapping A textura de bump mapping que nós utilizaremos armazenará as normais da superfície dos triângulos. Quando nós calcularmos a iluminação por pixel, nós usaremos a normal adquirida pelo bump map e com isso daremos riqueza de detalhe à superfície.

292 Um problemão no entanto
As normais da textura estão num espaço próprio delas. Chamado de espaço tangente. Ou nós convertemos a normal para o espaço de mundo, ou nós convertemos os outros vetores para o espaço tangente. Vamos converter todos os vetores para o espaço tangente.

293 Matriz de mudança de base
A matriz de mudança de base é quem vai converter do espaço do mundo (onde a gente tem calculado a iluminação até agora) para o espaço tangente. Ela é composta por 3 componentes: Vetor normal: A normal do triângulo. Vetor tangente: É um vetor novo, que deve vir em cada vértice, e deve ser tangente à face do triângulo. O vetor tangente é perpendicular à normal. Vetor Binormal: É calculado usando um produto vetorial entre a normal e a tangente. Todos esses vetores são normalizados.

294 Matriz de mudança de base
A cara da matriz é a seguinte: (tx, nx, bx) (ty, ny, by) (tz, nz, bz) Os vetores (da luz, do olho etc) são multiplicados por essa matriz da seguinte forma: (lx, ly, lz) * (tz, nz, bz) O exemplo acima multiplicou o vetor da luz no espaço de mundo pela matriz. O resultado é o vetor da luz no espaço tangente que nós podemos usar para calcular a iluminação normalmente. Mas porque essa matriz faz isso?

295 Matriz de mudança de base
O vetor resulta da multiplicação anterior é composto por 3 operações: x = dot(L,T) y = dot(L,N) z = dot(L,B) Lembra que ao fazer o dot de 2 vetores sendo que 1 é normalizado, você acha a projeção do outro vetor no vetor normalizado? (T, N e B são normalizados...) dot(L,T) calcula a projeção de L no vetor tangente. dot(L,N) calcula a projeção de L no vetor normal. dot(L,B) calcula a projeção de L no vetor binormal. Essas projeções geram um vetor nos eixos x, y e z se a tangente representar o eixo x, a normal representar o eixo y e a binormal representar o eixo z.

296 Matriz de mudança de base
Digamos que as normais são feitas da seguinte maneira: O mapa está deitado no eixo x,z As normais sem inclinação apontam para o eixo y. Esse é o espaço tangente. Os vetores Tangente, Normal e Binormal representam os vetores x,y e z porém no espaço de mundo. Ao projetar L no vetor tangente, você acha um valor que representa o tamanho de L no vetor tangente. Esse tamanho é o mesmo no eixo x. Logo ao projetar L em T, você acha L em x. Repetindo, T,N e B TEM QUE ESTAR NORMALIZADOS.

297 Vértices Além da posição, da normal, e da coordenada de textura, você agora precisa adicionar a tangente e, caso desejado, mais uma coordenada de textura para o bump map.

298 Vertex Shader

299 Pixel Shader

300 Exercício Implementar um triângulo com bump mapping.
Experimente gerar proceduralmente um mapa normal com algum formato.

301 Grafos de Cena Grafos de cena são estruturas de dados muito importantes que são utilizadas para diferentes propósitos. O propósito mais importantes do grafo de cena é hierarquia de transformações. Um grafo de cena geralmente tem o formato de uma árvore n-ária.

302 Grafo de Cena Por exemplo, digamos que você queira colocar uma arma no braço de um guerreiro. Para colocar o guerreiro no ambiente 3d, ele é transformado por sua matriz de mundo. Para colocar a arma no mundo, primeiro a arma é tranformada para a posição do guerreiro, e depois é transformada para a posição da mão do guerreiro.

303 Grafo de Cena

304 Grafo de Cena

305 Grafo de Cena Node: é a classe que permite implementar a hierarquia de transformações. Possui uma transformação como membro e uma lista de filhos. Mesh: é uma classe que implementa a renderização de algum objeto.

306 Grafo de Cena No exemplo da arma, poderíamos ter 2 malhas, uma representando o torso do guerreiro e outra representando a arma. A transformação da arma poderia ser uma translação +5 em x. Após definir a transformação de mundo do guerreiro, é renderizada a malha do guerreiro. Depois essa transformação é usada para calcular a transformação de mundo da arma.

307 Culling em cenários usando octrees
Os cenários dos jogos de hoje são complexos e detalhados. Logo, são pesados para renderizar. Porque enviar todo o cenário para a placa gráfica a cada frame, se na maioria das vezes você só consegue ver parte do cenário? A octree é uma estrutura de dados que permite reduzir o custo de renderização pois com ela é possível cálcular as regiões do cenário que são visíveis. A octree é uma árvore onde cada nó possui 8 filhos.

308 Culling usando octrees

309 Equação do plano A equação de um plano é definida geralmente de duas formas: (P – P0) * n = 0. Nesse caso P é um ponto qualquer, P0 é um ponto no plano e n é a normal normalizada do plano. Se P – P0 (que gera um vetor) dot n resultar em 0 é porque o ponto está no plano. Essa equação após distribuir os dots vira: P * n – P0 * n = 0 Ax + By + Cz + D = 0. Nesse caso A, B, C e D definem o plano. Se você colocar um ponto (x,y,z) nessa fórmula e ela der zero é porque o ponto está no plano. A,B,C correspondem aos componentes da normal e D = - P0 * n.

310 Calculando a distância de um ponto à um plano
O que representa P * n e P0 * n na equação P * n – P0 * n ?

311 Calculando a distância de um ponto à um plano
P0 * n = | P0 | * cos (theta) | P0 | * cos (theta) = tamanho de P0 projetado na normal = distância do plano à origem. P * n = | P | * cos (theta) | P | * cos (theta) = tamanho de P projetado na normal. P * n – P0 * n = Distância de P ao plano. Logo se essa distância for 0, o ponto está no plano. Se essa distância for positiva, o ponto está no lado do plano em que aponta a normal. Se essa distância for negativa, o ponto está no lado oposto ao qual a normal aponta.

312 Calculando a interseção de uma AABB com um plano
A idéia do algoritmo é calcular na AABB um mínimo e um máximo baseado na posição do plano e a seguir verificar as distâncias dos pontos min e max com relação ao plano.

313 Calculando a interseção de uma AABB com um plano

314 Verificando se uma AABB está dentro de um Frustum

315 Calculando um frustum

316 Calculando um frustum

317 Criando a Octree

318 Criando a octree

319 Criando a Octree

320 Criando a octree

321 Renderizando com a octree

322 Adicionando objetos à octree

323 Exercício Implemente um código que calcula uma AABB em um objeto e só renderiza esse objeto se sua AABB estiver interceptando o frustum. Implemente uma octree e adicione esse objeto à octree. Renderize usando a octree.

324 Quaternions Quaternions são ferramentas importantes para realizar rotações. Com multiplicação de quaternions você pode realizar rotações. Realizar uma interpolação entre duas rotações é “relativamente” simples com quaternions.

325 Quaternions Para entender quaternions, precisamos primeiro aprender sobre números complexos. Com o conjunto dos números reais dá pra representar uma quantidade considerável de números. Nós no entanto não conseguimos representar a raiz de -4. Porque nenhum número vezes ele mesmo dá um número negativo.

326 Números complexos Números complexos possuem uma parte real e uma parte imaginária como escrito abaixo: a + bi a = parte real. bi = parte imaginária. Então os números abaixo são todos números complexos: 4 + 2i 1 + 10i 5 4i

327 Números complexos A parte imaginária possui uma peculiaridade:
Ou seja: 2i * 2i = 4i^2 = -4 Com isso nós podemos encontrar a raiz de números negativos: raiz de -4 = raiz de 4 * i^2 = 2i raiz de -1 = raiz de i^2 = i

328 Quaternions Quaternions são números hiper-complexos (sério!).
Eles possuem 3 partes imaginárias e são escritos como: a + bi + cj + dk Então o número abaixo é um quaternion: 2 + 3i + 4j + 5k

329 Quaternions Assim como números complexos: i^2 = j^2 = k^2 = -1
Porém números complexos possuem algumas definições adicionais (aqui começa a loucura). i * j = k j * i = -k j * k = i k * j = -i k * i = j i * k = -j i * j * k = (i * j) * k = i * (j * k) = -1

330 Multiplicando dois quaternions
q0*q1 = (w0 + x0*i + y0*j + z0*k) * (w1 + x1*i + y1*j + z1*k). Vamos aplicar multiplicação distributiva: w0*w1 + w0*x1*i + w0*y1*j + w0*z1*k + x0*i*w1 + x0*i*x1*i + x0*i*y1*j + x0*i*z1*k + y0*j*w1 + y0*j*x1*i + y0*j*y1*j + y0*j*z1*k + z0*k*w1 + z0*k*x1*i + z0*k*y1*j + z0*k*z1*k (w0*w1 – x0*x1 – y0*y1 – z0*z1) w0*(x1*i + y1*j + z1*k) + w1*(x0*i + y0*j + z0*k) + x0*y1*k - x0*z1*j – y0*x1*k + y0*z1*i + z0*x1*j - z0*y1*i

331 Definições para facilitar a vida:
Um quaternion: q0 = (w0 + x0*i + y0*j + z0*k) = (w0 + v0) Ou seja: v0 = (x0*i + y0*j + z0*k) Outro quaternion: q1 = (w1 + x1*i + y1*j + z1*k) = (w1 + v1) Produto escalar: v0 * v1 = (x0*x1 + y0*y1 + z0*z1) Produto vetorial: v0 x v1 = (y0*z1*i – z0*y1*i + z0*x1*j – x0*z1*j + x0*y1*k – y0*x1*k) Escala: a * v0 = (a*x0*i + a*y0*j + a*z0*k)

332 Usando nossas definições
q0*q1 = (w0*w1 – x0*x1 – y0*y1 – z0*z1) w0*(x1*i + y1*j + z1*k) + w1*(x0*i + y0*j + z0*k) + x0*y1*k - x0*z1*j – y0*x1*k + y0*z1*i + z0*x1*j - z0*y1*i Torna-se: q0*q1 = w0*w1 – v0*v1 + w0*v1 + w1*v0 + v0 x v1

333 O conjugado de um quaternion
O conjugado de um quaternion é definido como: q0 = (w0 + v0) – quaternion normal q0* = (w0 – v0) – conjugado do quaternion

334 Rotacionando vetores Crie 1 quaternion com w = 0 e com os componentes x,y e z do próprio vetor que você quer rotacionar: (x, y, z) -> (0, x, y, z) = v Crie outro quaternion onde w = cos(theta/2), o x,y,z é o eixo de rotação normalizado e escalado por sin(theta/2): q = (cos(theta/2) + sin(theta/2)d) A multiplicação qvq* gera um quaternion cujos elementos x,y,z são o vetor 3D rotacionado pelo eixo d do quaternion q por theta graus.

335 Rotacionando vetores Ou seja, digamos que queremos rotacionar o vetor (1, 0, 0) no sentido anti-horário do eixo (0, 0, 1). Queremos rotacionar 90 graus gerando o vetor (0, 1, 0). Vamos criar dois quaternions v e q: v = (0, 1, 0, 0) q = (cos(45) + sin(45)(0, 0, 1)) = (cos(45), 0, 0, sin(45)) Logo: qvq* = (0, 0, 1, 0) = Rotacionamos o vetor!

336 Porque isso funciona? Para entender porque quaternions funcionam precisamos primeiro aprender como criar uma matriz que rotaciona um vetor dado um eixo qualquer.

337 Rotacionando vetores pelo eixo z
Para rotacionar um vetor em xy usando o eixo z usamos a matriz: (cos(theta) sin(theta) 0) (-sin(theta) cos(theta) 0) (vx vy vz) * ( ) Como podemos fazer para rotacionar um vetor por um eixo qualquer?

338 Matriz de Mudança de Base
Assim como no bump mapping vamos criar uma matriz que projeta o vetor em 3 outros vetores normalizados que formam uma base orthonormal. Um desses vetores base é o eixo de rotação. Os outros dois vetores podem ser dois vetores quaisquer ortogonais entre si.

339 O Algoritmo Criar a matriz de vetores base sendo que o vetor base que representa o eixo de rotação precisa ser a última coluna gerando o valor em z. A matriz abaixo mostra os vetores base a, b e rot, todos normalizados e rot é o eixo de rotação. (ax bx rotx) (ay by roty) (vx vy vz) * (az bz rotz) = (v*a v*b v*rot) Com essas projeções é possível achar o vetor em nosso espaço x,y,z padrão. Sendo que o eixo de rotação passa a representar o eixo z. Depois de multiplicar o vetor pela matriz acima, nós o multiplicamos pela matriz de rotação no eixo z mostrada anteriormente. Com isso o vetor rotaciona no eixo z que representa o eixo de rotação rot. Depois de rotacionar nosso vetor no eixos x,y,z padrão, devemos calcular como fica esse vetor rotacionado usando como vetores base os vetores a, b e rot. Para achar isso podemos calcular na mão, multiplicando os vetores a, b e rot pelos valores achados em x, y e z após a rotação: vfinal = vx * a + vy * b + vz * rot Ou podemos multiplicar o vetor rotacionado pela matriz inversa da matriz formada por a,b e rot.

340 O Algoritmo (Resumidamente)
V = vetor que queremos rotacionar RV = vetor rotacionado MB = Matriz criada a partir de A, B e Rot RotZ = Matriz que rotaciona pelo eixo Z MB^t = Matriz transposta da MB. Matriz Transposta = Inversa para rotações. O Algoritmo aplica a seguinte fórmula: RV = V * MB * RotZ * MB^t

341 Código

342 Código sem problemas de cosseno

343 Analisando a matriz de rotação
Vamos assumir que: cos(theta) = c sin(theta) = s R = (ax bx rotx) (c s 0) (ax ay az) (ay by roty) (-s c 0) (bx by bz) (az bz rotz) ( ) (rotx roty rotz) R = (a^t b^t rot^t) (c s 0) (a) (-s c 0) (b) ( ) (rot) R = (a^t b^t rot^t) (c*a+s*b) (-s*a+c*b) (rot) R = a^t(c*a+s*b) + b^t(-s*a+c*b) + rot^t*rot R = c*(a^t*a + b^t*b) + s*(a^t*b – b^t*a) + rot^t*rot

344 Algumas fórmulas v = (a*v)*a + (b*v)*b + (rot*v)*rot = x0*a + x1*b + x2*rot rot x v = rot x (x0*a + x1*b + x2*rot) = x0*(rot x a) + x1*(rot x b) + x2*(rot x rot) rot x v = x0*(rot x a) + x1*(rot x b) = x0*b – x1*a rot x v = x0*b – x1*a = (a*v)*b - (b*v)*a rot x v = (v*a^t)*b – (v*b^t)*a rot x v = v*(a^t*b – b^t*a)

345 Algumas Fórmulas v * I = v v * I = (a*v)a + (b*v)b + (rot*v)rot
v * I = (v*a^t)a + (v*b^t)b + (v*rot^t)rot v * I = v*(a^t*a + b^t*b + rot^t*rot) I = a^t*a + b^t*b + rot^t*rot Voltando a nossa fórmula original: R = c*(a^t*a + b^t*b) + s*(a^t*b – b^t*a) + rot^t*rot R = c*(I – rot^t*rot) + s*(a^t*b – b^t*a) + rot^t*rot R = cI – c*rot^*rot + s*(a^t*b – b^t*a) + rot^t*rot R = cI + s*(a^t*b – b^t*a) + (1 – c)rot^t*rot

346 Algumas fórmulas rot x (rot x v) = rot x (x0*b – x1*a) = x0*(rot x b) – x1*(rot x a) = -x0*a –x1*b v = (a*v)*a + (b*v)*b + (rot*v)*rot = x0*a + x1*b + x2*rot -x*a – x1*b = x2*rot – v = (rot*v)*rot - v rot x (rot x v) = (rot*v)*rot – v = v*(rot^t*rot) – v rot x (rot x v) + v = v*(rot^t*rot) Voltando à formula: R = cI + s*(a^t*b – b^t*a) + (1 – c)rot^t*rot Se a gente multiplicar um vetor por essa matriz: vR = cv*I + sv*(a^t*b – b^t*a) + (1 – c)*v*(rot^t*rot) vR = cv*I + s*(rot x v) + (1 – c)*(rot x (rot x v) + v) vR = cv + s*(rot x v) + (1 – c)*(rot x (rot x v)) + (1 – c)v vR = cv + s*(rot x v) + (1 – c)*(rot x (rot x v)) + v – cv vR = v + sin(theta)*(rot x v) + (1 – cos(theta))*(rot x (rot x v))

347 Ou Seja Quando você multiplica um vetor pela matriz RV abaixo:
RV = V * MB * RotZ * MB^t Você está fazendo na realidade o seguinte cálculo: vR = v + sin(theta)*(rot x v) + (1 – cos(theta))*(rot x (rot x v))

348 Código de rotação mais rápido

349 Voltando aos quaternions...
Foi dito que: dado um vetor v = (x, y, z) armazenado em um quaternion q0 = (0 + v) Um eixo de rotação normalizado r = (rx, ry, rz) armazenado em um quaternion da seguinte forma: q1 = (cos(theta/2) + sin(theta/2)*r) A multiplicação: q1q0q1* gera um quaternion cujos componente x, y, z consistem no vetor v rotacionado pelo eixo r por theta graus.

350 Vamos analisar essa multiplicação
Vamos considerar: s = sin(theta/2) c = cos(theta/2) Lembrando: q0*q1 = w0*w1 – v0*v1 + w0*v1 + w1*v0 + v0 x v1 q1q0q1* = (c + s*d)(0 + v)(c – s*d) q1q0q1* = (-s*d*v + c*v + s*d x v)(c – s*d) q1q0q1* = [-s*c*d*v – (c*v + s*d x v)*(-s*d)] + (-s*d*v)*(-s*d) + c*(c*v + s*d x v) + (c*v + s*d x v) x (-s*d) -s*c*d*v + s*c*d*v + s^2*(d x v)*d + s^2*(d*v)*d + c^2*v + s*c*(d x v) - s*c*(v x d) - s^2*(d x v) x d s^2*(d*v)*d + c^2*v + s*c*(d x v) + s*c*(d x v) - s^2*(d x v) x d s^2*(d*v)*d + c^2*v + 2*s*c*(d x v) - s^2*(d x v) x d s^2*(d*v)*d + c^2*v + 2*s*c*(d x v) + s^2*d x (d x v)

351 Continuando a análise s^2*(d*v)*d + c^2*v + 2*s*c*(d x v) + s^2*d x (d x v) (1 – s^2)*v + 2*s*c*(d x v) + s^2*[(d*v)*d + d x (d x v)] v – s^2*v + 2*s*c*(d x v) + s^2[(d*v)*d + d x (d x v)] v + 2*s*c*(d x v) + s^2[(d*v)*d – v + d x (d x v)] Porém: d x (d x v) = (d*v)*d – (d*d)*v = (d*v)*d – v. Logo: q1q0q1* = v + 2*s*c*(d x v) + 2*s^2*d x (d x v)

352 Continuando 2*s*c = 2*sen(theta/2)*cos(theta/2) = ?
Para resolver essa fórmula vamos provar a seguinte fórmula: sin(a + b) = sin(a)cos(b) + sin(b)cos(a)

353 Prova da identidade trigonométrica
RPQ = pi/2 – RQP = pi/2 – (pi/2 – RQO) = RQO = a OP = 1 PQ = sin(b) OQ = cos(b) AQ/OQ = sin(a). AQ = sin(a)cos(b). AQ = RB. PR/PQ = cos(a). PR = cos(a)sin(b) sin(a + b) = PB = RB + PR = sin(a)cos(b) + cos(a)sin(b) Logo: sin(theta) = sin(theta/2)cos(theta/2) + cos(theta/2)sin(theta/2) sin(theta) = 2sin(theta/2)cos(theta/2)

354 Outra Prova OP = 1 PQ = sin(b) OQ = cos(b)
OA/OQ = cos(a). OA = cos(a)cos(b). RQ/PQ = sin(a). RQ = sin(a)sin(b). RQ = BA. cos(a + b) = OB = OA – BA = cos(a)cos(b) – sin(a)sin(b) Logo cos(theta) = cos(theta/2)cos(theta/2) – sin(theta/2)sin(theta/2) cos(theta) = cos^2(theta/2) – sin^2(theta/2) E Ainda: cos(theta) = (1 – sin^2(theta/2)) – sin^2(theta/2) cos(theta) = 1 – 2*sin^2(theta/2) 1 – cos(theta) = 2*sin^2(theta/2)

355 Voltando à fórmula do quaternion
q1q0q1* = v + 2*s*c*(d x v) + 2*s^2*d x (d x v) q1q0q1*= v + sin(theta)*(d x v) + (1 – cos(theta))*d x (d x v) Comparando à fórmula de rotação por um eixo qualquer: vR = v + sin(theta)*(rot x v) + (1 – cos(theta))*(rot x (rot x v)) Podemos ver então que a multiplicação de quaternions q1q0q1* resulta na mesma equação da rotação de vetores por um eixo qualquer mostrada anteriormente.

356 Exercício Rotacione um vetor usando multiplicação de quaternions.

357 Tópicos para Programação Distribuída
Fundamentos de Distribuição Técnicas de Distribuição para Jogos

358 Modelos de Distribuição
Cliente / Servidor

359 Modelos de Distribuição
Peer to Peer

360 Cliente / Servidor Capaz de manter milhares de jogadores no mesmo jogo (Utilizado em Massively Multiplayer Games). Há necessidade de manter um servidor (Custos podem ser elevados).

361 Peer to Peer (Ponto a Ponto)
Armazena (geralmente) menos jogadores por instância de jogo. Não requer servidor de grande poder computacional.

362 TCP – Transmission Control Protocol
Protocolo complexo que implementa a comunicação como um fluxo de bytes que trafega entre dois pontos (Circuito Virtual). Garante a chegada das mensagens As mensagens chegam de forma ordenada Algoritmos para redução do número de mensagens Comunicação 1 x 1

363 TCP - Conexão Há necessidade de estabelecer conexão
Função connect() da API dos sockets.

364 TCP – Envio de Mensagens
Garantia de chegada

365 TCP – Término da conexão
Ocorre quando a função closesocket() é chamada.

366 UDP – User Datagram Protocol
Protocolo simples que implementa comunicação como um envio de mensagens de forma atômica. Não há garantia de chegada Mensagens podem chegar desordenada ou duplicadas Permite realização de broadcasts ou multicast (comunicação 1 x N) Não há necessidade de se conectar.

367 Sockets API criada para abstrair a conexão entre computadores em diferentes sistemas operacionais. Um socket é formado pela união IP + Porta, que define unicamente uma parte de uma conexão.

368 Função socket() Função que cria um socket.
SOCKET socket(int family, int type, int protocol) family: Define a família de protocolos utilizados. AF_INET = Protocolos convencionais da internet. type: Define o tipo de socket. SOCK_STREAM ou SOCK_DGRAM. protocol: Define o tipo de protocolo dentro da família. IPPROTO_TCP = TCP, IPPROTO_UDP = UDP.

369 Função socket() Exemplo:
SOCKET s = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP).

370 Função connect() Estabelece conexão
int connect(SOCKET socket, const struct sockaddr *serveraddr, int addrlen) socket: O socket do cliente serveraddr: O endereço (IP+Porta) do servidor. addrlen: O tamanho da estrutura serveraddr.

371 Estrutura sockaddr_in
Estrutura passada para a função connect. struct sockaddr_in {    short sin_family;    //AF_INET unsigned short sin_port;    struct   in_addr sin_addr;    char sin_zero[8]; }; Define o endereço de um socket remoto

372 Estrutura in_addr

373 Função connect() SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons(SERVER_PORT); addr.sin_addr.s_addr = inet_addr(“ ”); connect(s, (const sockaddr*)&addr, sizeof(addr));

374 Função bind() Função usada pelo servidor para definir um socket local
int bind(SOCKET s, const sockaddr *localaddr, int addrlen); s: o socket do servidor localaddr: o endereço local addrlen: o tamanho do endereço

375 Função bind() SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons(LIST_PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(server, (const serveraddr*)&addr, sizeof(addr));

376 Função listen() Define um socket como passivo. Informa o S.O. que deve aceitar conexões direcionadas à esse socket. int listen(SOCKET s, int backlog); s: o socket do servidor backlog: O número de conexões que podem ficar pendentes para esse socket.

377 Função listen() //continuação do código da bind() listen(server, 8);

378 Função accept() Chamada por um servidor TCP para retornar a próxima conexão completa do ínicio da fila de conexões completas. SOCKET accept(SOCKET s, struct sockaddr * addr, int * addrlen); s: o socket do servidor addr: o endereço do novo cliente addrlen: o tamanho do endereço Essa função retorna o socket do novo cliente e bloqueia quando não há conexões terminadas.

379 Função accept() while(true) { addrlen = sizeof(addr);
cli_socket = accept(server, (...)&addr, &addrlen); ProcessClientSocket(cli_socket); }

380 Função closesocket() Libera os recursos alocados ao socket.
int closesocket(SOCKET s);

381 Função send() função que envia bytes usando um socket conectado.
int send(SOCKET s, const char * buffer, int len, int flags); s: o socket conectado por onde enviar os dados buffer: os bytes a serem enviados len: o número de bytes a serem enviados flags: Flags

382 Função send() //continuando o código da connect char msg[]= “coéeee!”;
send(s, msg, strlen(msg)+1, 0);

383 Função recv() Função usada para receber dados de uma conexão.
int recv(SOCKET s, char * buffer, int len, int flags);

384 Função recv() //continuando o codigo da send() char reply[64];
recv(s, reply, 64, 0); cout << reply;

385 Função sendto() Usada com sockets UDP não conectados
int sendto(SOCKET s, const char *buf, int len, int flags, const struct *destaddr, int addrlen);

386 Função recvfrom() Função usada em servidores UDP sem conexões.
int recvfrom(SOCKET s, char * buffer, int len, int flags, sockaddr *addr, int *addrlen);

387 Para usar a API de sockets no windows
Para inicializar a DLL do sockets use: Para finalizar a DLL: Inclua a lib: ws2_32.lib Header file: winsock2.h

388 Exercício Implementar uma aplicação cliente / servidor usando TCP onde o cliente envia uma string para o servidor e o servidor escreve na tela aquilo que recebeu. O servidor só aceita 1 cliente de cada vez.

389 Exercício Implementar uma aplicação cliente / servidor semelhante ao exercício 1 porém usando UDP.

390 Livros Importantes TCP/IP Illustrated, Vol 1
Unix Network Programming, Vol 1 (Ambos do W. Richard Stevens!)

391 Técnicas de Distribuição para Jogos

392 Multithreading (Revisão)
Conhecimento fundamental (Xbox 360). Thread = Parte de um programa que executa concorrentemente ou paralelamente a outras partes do programa. Comunicação entre partes é feita através de variáveis compartilhadas. Um game = Thread Principal (Main) + Outras Threads

393 Multithreading Múltiplas threads parecem executar paralelamente mesmo em um único processador devido ao time slicing.

394 Multithreading (Exemplo)

395 Criando uma thread uintptr_t _beginthread(
void( *start_address )( void * ), unsigned stack_size, void *arglist ); start_address: endereço de uma função que inicia a execução da thread. stack_size: tamanho da pilha da nova thread. arglist: ponteiro para parâmetro passado para thread.

396 Criando uma thread int A = 0; void Add(void * data) { A++; }
void main() _beginthread(Add, 0, NULL); //tente comentar essa linha Sleep(1000); cout << A; char c; cin >> c;

397 Case Study: Project Gotham Racing
Core Thread Software threads Update, physics, rendering, UI 1 Audio update, networking Crowd update, texture decompression Texture decompression 2 XAudio This title is also on Xbox 360. Things to notice: rendering on same thread as update. Two decompression threads. One unused thread, to leave all cycles to audio. Audio was a problem in this title. The update thread and crowd update threads both need to trigger sounds, which required grabbing a critical section that the Audio update thread was often holding.

398 Exercício Evolua o exercício 1 de forma que o servidor se torne capaz de processar múltiplos clientes simultaneamente. Utilize 1 thread por cliente.

399 Técnicas usando TCP, UDP e Sockets

400 Entendendo TCP O Algoritmo de Naggle e o Delayed Ack.
Desligando o algoritmo BOOL b = TRUE; setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*)&b, sizeof(bool));

401 Usando sockets UDP conectados
sendto() em alguns sistemas operacionais realiza conexão, envio e desconexão. Permite receber mensagens ICMP.

402 Enviando múltiplos buffers em Sockets
WSABUF MsgBuf[2]; MsgBuf[0].buf = &Cabecalho; MsgBuf[0].len = sizeof(Cabecalho); MsgBuf[1].buf = MsgChat; MsgBuf[1].len = strlen(MsgChat); WSASend(Socket, MsgBuf, 2, &BytesSent, Flags, NULL, NULL);

403 Implementando Garantia de Chegada
Como calcular o RTO (Retransmission Timeout Timer) ? Levar em consideração o tempo estimado de round trip time (SRTT) e a variação estimada dos tempos de round trip time (VARRTT).

404 Implementando Garantia de Chegada
Calculando RTT estimado (SRTT): delta = RTT – SRTT SRTT = SRTT + (delta / RTTINF) Calculando variação de RTT (VARRTT): VARRTT = VARRTT + ((| delta | - VARRTT) / VARRTTINF) Calculando RTO: RTO = SRTT + VARRTT * VARINC

405 Exercício (Para casa) Implemente um protocol de reliable UDP.

406 Técnicas para Ambientes Virtuais Distribuídos

407 Dead-Reckoning O conceito de Dead-Reckoning Forma normalmente usada:
pos = pos0 + (vel * dir) * deltat Com Dead-Reckoning baseado a objetivos há a possibilidade de redução ainda maior no número de mensagens trocadas. Ex: “Desejo ir para o destino x,y”.

408 Sincronização baseada em tempo de comando
Alterando velocidades é possível fazer uma sincronização visual. T 0s T 1s T 2s

409 Eliminando Informações Desnecessárias
Geralmente, não há necessidade de enviar informações sobre objetos não perceptíveis.

410 Balanceamento Para conseguir processar um MMORPG é necessário quebrar o processamento entre diversos servidores.

411 Outras técnicas Nível de detalhe Agregação de Mensagens

412 Exemplo de Servidor MMORPG

413 Construindo a Rede de servidores
A ferramenta permite dividir responsabilidades entre os servidores

414 Diagrama de Classes

415 Outras características
IOCP Culling usando Grid Balanceamento usando retângulos Reliable UDP + Last Update Sincronização por Tempo de Comando Dead Reckoning baseado em objetivos

416 Resultados

417 Exercício Implemente uma demo onde existem 2 retângulos e cada retângulo sobre movimentação de um cliente diferente. Ao mover um retângulo, o outro cliente deve ver a movimentação. Implemente dead reckoning para reduzir o número de mensagens trocadas.

418 Tópicos para Física Teste de Interseção Raio – Plano
Teste de Interseção Raio - Triângulo Teste de Interseção Esfera – Esfera Teste de Interseção Elipsóide - Elipsóide Teste de Interseção AABB – AABB Detecção de Colisão em cenário estático usando elipsóides Resposta à Colisão Picking Otimizando a colisão com Binary Space Partition Trees. Dinâmica Linear Dinâmica Rotacional

419 Interseção Raio - Plano
Um plano pode ser expressado através da fórmula: N dot (P – P0) = 0 Onde N é a normal do plano, P é um ponto qualquer e P0 é um ponto no plano. Qualquer P colocado na fórmula que dê zero como resultado está no plano.

420 Interseção Raio - Plano
Um raio pode ser expressado através da seguinte fórmula: O + tD Onde O é a origem do raio, D é a direção normalizada e t é um valor que permite achar qualquer ponto na extensão do raio.

421 Interseção Raio - Plano
Devemos achar um t que gere um ponto que quando utilizado na fórmula do plano resulte em zero. Ou seja, devemos achar um t que resulte em um ponto no plano. t vai indicar a distância percorrida da origem ao local de interseção.

422 Interseção Raio - Plano
O + tD = P N dot (P – P0) = 0 N dot (O + tD – P0) = 0 N*O + tN*D – N*P0 = 0 tN*D = N*P0 – N*O t = (N*P0 – N*O) / N*D t = N*(P0 – O) / N*D Quando N*D for igual a zero, o raio é paralelo ao plano.

423 Interseção Raio - Plano

424 Interseção Raio - Triângulo
Ache a interseção do raio com o plano do triângulo. Ache uma componente na normal diferente de zero, projete em um plano do eixo alinhado de acordo com a componente isso transforma nosso problema em 2D. Use os coeficientes angulares entre o ponto e os vértices para verificar se o ponto está dentro do triângulo.

425 Interseção Raio - Triângulo
Para achar a relação entre o coeficiente angular do ponto e uma aresta, testa-se a seguinte condição: (v2y–v1y) / (v2x–v1x) < (py–v1y) / (px–v1x) (v2y-v1y)*(px-v1x) < (py-v1y)*(v2x-v1x) 0 < (py-v1y)*(v2x-v1x) - (v2y-v1y)*(px-v1x)

426 Interseção Raio - Triângulo

427 Interseção Raio - Triângulo

428 Interseção Esfera - Esfera
Um esfera é representada computacionalmente através de uma posição C que define o centro da esfera e um tamanho r que define o raio da esfera. Duas esferas estão colidindo se a distância entre os centros for menor que a soma dos raios das esferas, ou seja: Está colidindo se: | C1 – C2 | < r1 + r2 Onde C1 e C2 são os centros das esferas 1 e 2 e r1 e r2 são os raios das esferas 1 e 2.

429 Interseção Esfera - Esfera

430 Interseção Elipsóide - Elipsóide
O que diabos é um elipsoíde? Elipsóide é como uma esfera que possui um raio de tamanho diferente para cada eixo.

431 Interseção Elipsóide - Elipsóide
A interseção de elipsóides é muito parecida com a de esferas. Dois elipsóides estão colidindo se: | C1 – C2 | < | R1 | + | R2 | Onde C1 e C2 são os centros dos elipsóides. R1 é o vetor que vai de C1 até a superfície da elipsóide 1 na direção de C2. R2 é similar porém indo na direção de C2 para C1.

432 Interseção Elipsóide - Elipsóide
O tamanho do raio de uma elipsóide varia dependendo da direção, logo como achar o tamanho de R1 e R2?

433 Interseção Elipsóide - Elipsóide
Ou seja, após achar a direção, normalizar e depois multiplicar pelos tamanhos dos raios nos eixos x, y e z.

434 Interseção Elipsóide - Elipsóide

435 Interseção AABB - AABB AABB significa Axis Aligned Bounding Box, ou seja, representa uma caixa com os alinhada aos eixos x,y e z.

436 Interseção AABB - AABB AABBs são representadas por um ponto mínimo e um ponto máximo. Duas AABBs estão colidindo se em nenhum momento algum dos testes triviais de rejeição retornam sucesso.

437 Interseção AABB - AABB

438 Exercício Implemente uma aplicação que renderiza dois cubos.
Coloque duas AABBs envolvendo cada um dos cubos. Faça um dos cubos se moverem com o uso de setas. Implemente a detecção de colisão entre os cubos e impeça que os cubos entrem um no outro.

439 Detecção de Colisão em cenário estático usando Elipsóides
Como podemos colidir elipsóides com triângulos?

440 Colisão com cenários estáticos
Para detectar colisão: Lance um raio do centro da elipsóide na direção do triângulo usando a normal negativa do triângulo como direção.

441 Colisão com cenário estáticos
Calcule o tamanho do raio nessa direção usada. Se o raio acerta o triângulo e sua distância para o plano é menor que o raio da elipsóide na direção usada, a elipsóide colide com o triângulo.

442 Colisão com cenários estáticos

443 Resposta à colisão Após detectar a colisão temos que tratar a resposta à colisão. Uma resposta normalmente dada é empurrar o objeto para fora da geometria em que ele colidiu. Como vamos empurrar a elipsóide para fora do triângulo?

444 Resposta a colisão Nós empurramos o centro da elipsóide para fora na direção da normal do plano. A distância empurrada = Tamanho raio – Distância centro-plano.

445 Resposta à colisão

446 Exercício Implemente uma aplicação onde no cenário há um triângulo.
Coloque um elipsóide como BV da câmera. Faça a câmera se mover e colidir com o triângulo.

447 Picking Algo bastante utilizado em games é a seleção de algum objeto 3d com o uso do mouse. Para conseguirmos descobrir o objeto selecionado, precisamos lançar um raio no espaço de mundo. Para descobrirmos esse raio precisamos saber onde no mundo se encontra a posição selecionada na tela. Com essa informação podemos lançar o raio partindo da câmera e passando por essa posição 3D no mundo.

448 Picking Para achar a posição no espaço 3D usaremos as informações para a criação do frustum de visão. Para achar a metade da altura da tela em espaço de mundo você calcula(usando os valores da imagem como exemplo): tg(15) = x / near tg(15) * near = x; Para achar a metade da largura basta fazer: half_width = x * aspect; aspect é a divisão da largura da tela pela altura. Na prática, o valor 15 da figura será FOVy / 2 (FOVy é o mesmo valor passado na função de criação de matriz de perspectiva).

449 Picking

450 Binary Space Partition Trees
BSPs são árvores binárias muito parecidas com as árvores binárias de busca. (Na realidade, a árvore binária de busca pode ser vista como uma BSP de 1 dimensão). BSPs são úteis para acelerar a detecção de colisão.

451 Binary Space Partition Trees
Uma BSP é uma árvore onde cada nó representa um plano. Esse plano corta o espaço em 2. Os planos dos filhos a direita e esquerda cortam os espaços resultantes da direita e da esquerda.

452 Binary Space Partition Trees
Para detectar a colisão de um objeto com outros: Adicione todos os objetos à BSP. Os objetos terminam em listas nas folhas da árvore. Detectar colisão de cada objeto com todos os outros objetos da mesma folha.

453 Exercício (Para Casa) Implementar uma BSP.
Adicionar objetos usando AABBs. Implemente a detecção de colisão de cada objeto com os outros da mesma folha.

454 Dinâmica Linear Como podemos mover objetos linearmente de maneira fisicamente realista? Aplicamos a segunda lei de Newton: F = M * A Dessa forma: F / M = A Onde F é força, M é massa e A é aceleração. F e A são vetores, M é um escalar.

455 O tempo e a posição Aceleração = variação de velocidade / variação de tempo Velocidade = variação de posição / variação de tempo Posição = área da função da velocidade em uma faixa de tempo Velocidade = área da função da aceleração em uma faixa de tempo

456 Um problema complicado
Dada uma aceleração, temos que achar a variação da velocidade e depois a variação da posição. O problema é que isso envolve uma integração de uma função (e nós não sabemos a função previamente ).

457 Método de Euler Consiste no método mais simples para achar as áreas.
Nele você adquire o valor no início do intervalo e considera ele constante durante todo o intervalo.

458 Método de Euler Para adquirir a posição: Aceleração = força / massa
Posição = posição + velocidade * variação de tempo Velocidade = velocidade + aceleração * variação de tempo

459 Implementando o método de Euler

460 Dinâmica Rotacional Nós aprendemos a mexer linearmente um objeto. Mas e quanto a rotação? Torque é o equivalente da força para o movimento rotacional. Para achar o torque de um corpo rígido, ache o produto vetorial entre o vetor que vai do centro de massa do objeto até o local onde está sendo aplicada uma força e o vetor da força.

461 Descobrindo a rotação Torque = Cross(R, F)
Aceleração Angular = Torque / Inércia Rotação = Rotação + Velocidade Angular * variação de tempo. Velocidade Angular = Velocidade Angular + Aceleração Angular * variação de tempo.

462 Descobrindo a rotação

463 Exercício Implemente um demo que renderiza uma caixa.
Implemente física linear aplicando uma força constante na caixa. Implemente física rotacional causando a caixa rotacional dependendo do lugar onde se aplica a força.

464 Tópicos para Inteligência Artificial
Máquinas de Estados Embedding Lua na sua aplicação Path Finding

465 Máquinas de Estado Máquinas de Estado são uma das principais técnicas de inteligência artificial usadas desde o primórdio dos tempos (~1980). Máquinas de Estado são máquinas abstratas compostas por estados e eventos que geram transições nesses estados. Elas são úteis pois permitem a criação de seres que possuem comportamento dependendo do estado e que variam esse comportamento dependendo de eventos que ocorrem no jogo.

466 Máquinas de Estado Um soldado em um jogo pode ter a seguinte máquina de estado:

467 Maquinas de estado

468 Maquina de estado

469 Máquinas de Estado Paralelas
Quando uma determinada entidade exige um comportamento sofisticado sua máquina de estado tende a ficar complexa demais. A solução pra isso é fazer uma mesma entidade usar múltiplas máquinas paralelas para resolver diferentes problemas. Por exemplo: Digamos que aquele inimigo do exemplo anterior passe a usar uma metralhadora e 2 máquinas de estado, uma para movimentação e outra para atacar.

470 Maquina de estado de movimentação

471 Maquina de estado de combate

472 Máquinas de estado paralelas
Implemente da mesma forma que a máquina de estado normal, porém use 1 variável de estado por máquina e 1 switch por máquina.

473 Sincronizando máquinas de estado
É interessante às vezes fazer com que máquinas de estado façam interações. Half-life fez isso com seus soldados que se coordenavam ao combater o player. Para fazer essa comunicação basta usar variáveis compartilhadas como variáveis estáticas.

474 Sincronizando máquinas de estado
Digamos que nós queremos que um inimigo ao avistar o player avise aos outros inimigos da localização do player e todos os outros inimigos a seguir se movem para a região avisada.

475 Máquina de estado de movimentação

476 Exercício Implemente um demo com 2 caixas. Uma caixa é o inimigo e se move pela tela seguindo por 4 posições pré-definidas. A outra é controlada pelo jogador. Quando a caixa do jogador se aproxima à uma certa distância. O inimigo começa a se aproximar até que ele chega à uma distância e pára. Se o jogador se afastar depois do inimigo, o inimigo volta a caminhar no seu caminho pré-definido. Implemente esse demo usando uma máquina de estados.

477 Embedding Lua na sua aplicação
Scripting é uma adição fundamental à qualquer engine. Scripting fornece uma ambiente sandbox para programadores de gameplay e de inteligência artificial onde eles podem criar lógica e conteúdo sem se preocupar em quebrar nada interno da engine. Ou seja, Scripting fornece permite que um trabalho puramente criativo seja realizado. Scripting também é necessário se você deseja que seus jogos sejam “modable”.

478 Embedding Lua

479 Embedding Lua A primeira linha cria o objeto de estado lua que é usado em todas as funções. A função dofile é quem executa scripts. No final lua_close deve ser chamado para liberar recursos.

480 Escrevendo e lendo valores

481 Escrevendo e lendo valores
Script em lua: Resultado: x = 7 e y = 9

482 Escrevendo e lendo valores
Lua utiliza de uma pilha para trocar valores com o código em C++. A função setglobal define o nome de uma variável e a pushnumber seu valor. A função getglobal coloca uma variável no topo da pilha que pode ser capturada com a função tonumber que recebe o estado lua e o índice que se deseja usar para pegar um valor.

483 Adicionando funções à Lua
Scripts são úteis para customização de comportamentos, implementação de lógica de jogo e AI. Porém scripts são mais lentos que código compilado em C++, portanto para certas computações custosas é valido implementá-las em C++ e adicionar a função à Lua.

484 Adicionando funções a Lua

485 Adicionando funções a Lua

486 Adicionando funções a lua
A função register registra uma função em lua. Dentro da função faz-se uso da pilha normalmente para acessar valores. Para retornar valores usa-se push e o valor retornado define o numero de valores a serem retornados (É possível retornar mais de um valor).

487 Exercício Reimplemente o exemplo anterior executando a máquina de estados em Lua ao invés de executá-la em C++. Adicione à lua a função de distância implementada em C++.

488 Path Finding Um problema muito comum em diversos jogos é a necessidade de calcular o caminho para se mover de um ponto a outro do mapa. Para resolver esse problema nós usaremos o algoritmo A*.

489 A* O algoritmo A* é um algoritmo que implementa uma busca heurística.
Uma busca heurística leva em consideração alguma informação antes de tomar uma decisão. No nosso caso, o A* levará em consideração informações geométricas para tomar decisões.

490 Min-Heap O algoritmo do A* precisa de uma estrutura de dados que implemente uma fila de prioridade. Uma fila de prioridade implementa uma função para retornar o elemento mais prioritário de forma eficiente. Uma forma de implementar uma fila de prioridade é usar a estrutura Min-Heap.

491 Min-Heap Uma Min-Heap é semelhante à uma árvore binária que possui a seguinte regra: Cada nó é menor que seus filhos. A Min-Heap pode ser implementada em um vetor.

492 PriorityQueue

493 PriorityQueue

494 PriorityQueue

495 PriorityQueue

496 PriorityQueue

497 A* A prioridade de um nó é calculada usando 2 informações.
A distância percorrida até o nó = g. A distância do nó atual até o nó destino = h. A prioridade é então calculada: f = g + h Essas prioridades fazem o algoritmo selecionar os nós que se aproximam mais rapidamente do nó destino.

498 A*

499 A*

500 Exercício Implemente um PathFinder usando A* que recebe uma matriz de inteiros onde 1 é lugar passável e 0 é parede, e a partir dessa matriz é criado uma estrutura de dados onde cada nó possui norte, sul, leste e oeste. Faça o PathFinder calcular o caminho entre duas posições. Imprima as posições numa console application.

501 Fundamentos de Programação de Jogos
Arquitetura de Tecnologias de Jogos

502 Arquitetura de Tecnologia de Jogos
Jogos 3D possuem a vantagem de poderem compartilhar muita tecnologia. Isso aumenta a recompensa em implementar uma engine de jogos. Engines de jogos podem possuir designs completamente diferentes, logo o design mostrado aqui não deve ser considerado o design supremo e fundamental de engines.

503 Implementando um Jogo Uma forma interessante de dividir os diferentes momentos de um jogo é implementando-o como uma máquina de estados. Cada estado é implementado em uma classe diferente.

504 Implementando um Jogo

505 Loop Principal Uma forma, para jogos single player é a seguinte:
Renderiza a cena (Sem dar Present) Adquire e Processa Input do Player Processa AI Processamento física, testa colisão e resposta Processamentos extras, específicos do estado atual Present

506 Game Engine Todo List (Single Player)
1 Level Loader que carrega o arquivo com a configuração do level. Esse arquivo aponta para outros arquivos que deve ser carregados para a execução do level. 1 engine 3d para carregar e renderizar o cenário com efeitos especiais de maneira rápida. 1 engine de física/colisão com estruturas otimizadas para detecção de colisão. Esse engine move objetos e detecta colisão e repassa o tratamento da resposta para o game. 1 engine de input para aquisição do input. 1 engine de AI que carrega dados em um cenário específicos para AI e processa as entidades AI do game. Todos são Singletons.

507 Level Loader Tem como missão carregar o arquivo que representa o level. Esse arquivo possui os nomes de todos os outros arquivos que serão usados no level. Ex: O arquivo da octree do cenário. O arquivo que armazena os waypoints do cenário. O arquivo que armazena a estrutura usada para processar a colisão. Os modelos, os scripts. O level loader repassa todos esses nomes para as respectivas engines carregarem.

508 Engine 3D Responsável pela parte gráfica do jogo.
Carrega o cenário e os modelos 3D. Pode carregar uma estrutura especial como uma Octree. Carrega também a parte gráfica dos elementos do cenário. Renderiza o cenário 3D e os modelos usando técnicas de aceleração e efeitos especiais.

509 Engine 3D

510 Engine 3D (Extras) É interessante criar plugins para 3DS Max para exportar cenário e modelos. É importantíssimo implementar um level editor onde as fases são feitas a la wysiwyg onde você define locais onde ficam as entidades, triggers e scripts especiais. Você também configura características gráficas específicas da engine e vê em tempo real as alterações.

511 Engine de Física / Colisão
Carrega a estrutura de colisão e a parte física dos elementos da cena. Pode carregar por exemplo uma BSP. Processa a física das entidades e usa técnicas de aceleração para os testes de colisão. Delega a resposta de colisão ao game. Isso pode ser feito com ponteiros para função ou funções virtuais.

512 Engine de Física / Colisão

513 Engine de Input Wrapper de funções que fazem polling de dispositivos.

514 Engine de AI Carrega a estrutura de dados necessária para executar a AI. Como exemplo, carregar os caminhos possíveis pela AI. Carrega a parte de AI das entidades da cena. Processa movimentação e inteligência usando C++ e delegando para Lua partes do processamento.

515 Engine de AI

516 Problema O AI Engine decide mover um NPC para alguma região. Ele então requisita a movimentação para o Engine de Física. O Engine de física o move normalmente. Como o engine gráfico recebe essa alteração? Uma solução é fazer uso de IDs. Um elemento é identificado nas 3 engines através do uso de um mesmo ID. Logo se o engine de AI decidir mover um personagem, ele identifica o personagem através de seu ID. O engine gráfico antes da renderização requisita todos os elementos que se moveram para atualizar suas posições.

517 Elementos e IDs

518 Exercício Final Implementar um Jogo 3D


Carregar ppt "Curso Professional Lucas Euzébio Machado."

Apresentações semelhantes


Anúncios Google