410 likes | 558 Views
Simulação de Sistemas de Partículas com GPU. Yalmar Ponce. Visão Geral (roteiro). Sistemas de Partículas Que é um sistema de partículas? Usos dos sistemas de partículas Simulação básica de sistemas de partículas Métodos de integração Sistemas de partículas no GPU
E N D
Simulação deSistemas de Partículas comGPU Yalmar Ponce
Visão Geral (roteiro) • Sistemas de Partículas • Que é um sistema de partículas? • Usos dos sistemas de partículas • Simulação básica de sistemas de partículas • Métodos de integração • Sistemas de partículas no GPU • Simulação e renderização sem manter o estado • Simulação física preservando o estado • Alguns métodos para transferência de dados de textura para dados de vértices
Sistemas de Partículas • As primeiras aplicações foram em jogos • 1962 “Spacewar” • Uso de nuvens de pixeis para simular explosões • O movimento é randômico • 1978 “Asteroides” • Explosões usando movimentos de vetores • Provavelmente o primeiro jogo que usou “física” na simulação • 1983 “Star Trek II” • Simulação do movimento com efeitos especiais • O primeiro artigo de CG sobre sistemas de partículas • Ate hoje os conceitos não foram alterados
Que é um sistema de partículas? • Pontos de massa individuais que se movem no espaço 2D/3D • Forças e restrições definem o movimento • Alguns valores iniciais (e.g. posições) são obtidos usando métodos randômicos ou construídos. • Freqüentemente renderizados como primitivasde geometria individuais.
Usos dos sistemas de partículas • Explosões • Água • Fogo • Fumaça • neblina • Simulação de corpos • Efeitos após do impacto
Simulação básica de sistemas de partículas • Partículas são pontos no espaço 2D/3D • Forças (e.g. gravidade, vento, de impacto) aceleram uma partícula • Aceleração muda a velocidade • Velocidade muda a posição
Integrador de Euler • Precisa das seguintes propriedades • tpasso do tempo • a aceleração • vvelocidade • v´ velocidade anterior • p posição • p´ posição anterior • Integrando a velocidade v = v´ + a.t
Integrador de Euler • Integrando a posição p = p´ + v.t • Computacionalmente simples • Precisa armazenar a posição e velocidade da partícula
Integrador de Verlet • Integrando a posição p = 2.p´ - p´´+ v.t² • Onde p´´ e a posição previa à anterior • Não precisa armazenar a velocidade • O passo de tempo precisa ser (quase) constante • A velocidade pode ser aproximada
Sistemas de partículas no GPU • Simulação sem estado • Efeitos simples • Feita no “vertex shader” • Disponível desde a primeira geração de GPUs programáveis • Simulação preservando o estado das partículas • Recentemente pesquisado (2003+) • Simulação é feita com o auxilio de texturas • Freqüentemente o processo é feito no “fragment shader” • Disponivel só nas GPUs NVIDIA FX 5xxx+ e ATI 9500+
Simulação sem estado • Não armazena variações de estado nas partículas • Avalia funções fechadas que descrevem as mudanças do movimento/atributos • Dados computados dependem só de valores iniciais e uma descrição estática do ambiente
Simulação preservando o estado • Posições e velocidades devem ser armazenadas em texturas • Estas texturas em cada passo de tempo da simulação são renderizadas em outras texturas de igual tamanho • O “Fragment shader” executa a integração iterativa • Posições de textura são “re-interpretados” como dados de vértices e pode-se renderizar grupos de vértices, triângulos ou retângulos
Armazenamento do estado de uma partícula • Textura de posições • Textura de velocidades
Armazenamento do estado de uma partícula • Posição e velocidade são armazenados em texturas • Texturas 1D, 2D ou 3D podem ser usadas dependendo da aplicação e os requerimentos • Precisão: • GL_FLOAT
Operações de velocidade • Atualizar a textura de velocidades com varias operações: • Forças globais (vento, gravidade) • Forças locais (baseada em distancia, e.g. molas) • Forças de amortecimento • Forças de impacto • Após de ter a força total aplica-se a segunda lei de Newton (F = m.a) para obter a acelerção • Idêntico se as partículas tem massa unitária
Operações de velocidade • Impacto / Colisão em GPU é limitada para ter maior eficiência • Quadricas • Mapas de altura, e.g. terrenos • Algoritmo • Detectar colisão • Determinar normal à superfície no ponto de impacto • Determinar profundidade de penetração • Resposta à colisão alterando a velocidade
Resposta à colisão • Velocidade é dividida em duas partes • vn a velocidade normal • vt a velocidade tangencial vn = (v’.n)v’ vt = v’-vn Fricção pode ser aplicado n v vt vn
Atualização da posição • Integração Euler • Integração Verlet • Acumular as forças (força total) • Atualizar posições • Resolver restrições de distância • Colisão é tratado por projeção (e.g. partículas fora do mundo são projetados para dentro)
Transferindo dados de textura para dados de vértice • Após um passo da simulação precisamos transferir os dados da textura de posições para dados geométricos • Sistemas de partículas simples permitem uma eficiente transferência • Se o sistema de partículas representa um corpo pode ser preciso replicar vértices para interpretar o corpo • Existem diversos métodos para transferência de dados
Uber-Buffer (também chamado Super Buffer) • Armazena dados na memória da placa gráfica • Copia de um buffer (textura) para outro buffer (vertex buffer) • Disponible em placas NVIDIA GF FXxxxx+ e ATI R9xxx+ • Precisa de varias extensões OpenGL: • ARB_super_buffer, • NV_pixel_buffer_object • NV_pixel_data_range
Texturas de vértices • Acesso a texturas desde vertex shaders • (Problemas na placa gráfica ATI ???) • Vertex shader lê as posições das partículas • Disponible em OpenGL com (ARB_vertex_shader/GLSL)
Métodos alternativos • Como renderizar imagens de saída numa textura? • glReadPixels() glTexImage*() ? Lento. • glCopyTexImage*() Melhor. • glCopyTexSubImage*() Melhor ainda. • Renderizar diretamente em Texture (wglBindTexImageARB ) Elimina “copia de textura” potencialmente eficiente
Renderizando diretamente em Textura • Não faz parte do coração do OpenGL, mas as extensões ARB o tornam possível na maioria de GPUs. • Extensões requeridas: • WGL_ARB_extensions_string • WGL_ARB_render_texture • WGL_ARG_PBUFFER • WGL_ARB_pixel_format • Disponíveis em todas as placas gráficas NVIDIA GeForce
Renderizando diretamente em Textura: “Uma visão geral” • A idéia básica: • Um p-buffer para ser ligado como uma textura • Criar um objeto de textura • Criar um “Textura para renderizar” (quer dizer, um pixel buffer) • Algoritmo: • O PBuffer deve ser o destino atual para renderizar • Renderizar uma imagen • A janela deve ser destino atual para renderizar • Ligar o PBuffer para que seja o objeto texture • Usar o objeto textura como se fosse qualquer outro • Desligar o PBuffer do objeto texture
Criando o objeto textura • Exatamente como se fosse uma textura –não precisa especificar os dados da textura // Create a render texture object glGenTextures( 1, &render_texture ); glBindTexture( GL_TEXTURE_2D, render_texture ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
Criando o PBuffer • Visão Geral • Obter um contexto valido HDC hdc = wglGetCurrentDC(); • Escolher um pixel format • Especificamente um conjunto minimo de atributos • Color bits, Depth bits, Stencil bits, single/double, etc. • Deve-se especificar também • WGL_DRAW_TO_PBUFFER and algum de • WGL_BIND_TO_TEXTURE_RGB_ARB or • WGL_BIND_TO_TEXTURE_RGBA_ARB como TRUE. • Então executamos wglChoosePixelFormat()
Criando o PBuffer • Retorna uma lista of formatos o qual é adequado aos requerimentos minimos • Escolha um formato na lista • Criar o PBbuffer HPBuffer hbuf = wglCreatePBufferARB( hdc, fid, width, height, attr ); width e height são as dimensões do PBuffer “attr” é uma lista de outros atributos para o PBuffer. • Ajustar WGL_TEXTURE_FORMAT_ARB: • WGL_TEXTURE_RGB_ARB ou WGL_TEXTURE_RGBA_ARB • Ajustar WGL_TEXTURE_TARGET_ARB: • WGL_TEXTURE_1D_ARB, WGL_TEXTURE_2D_ARB, ou WGL_TEXTURE_CUBE_MAP_ARB • Ajustar WGL_MIPMAP_TEXTURE_ARB a um valor não zero para pedir espaço para os mipmaps. • Ajustar WGL_PBUFFER_LARGEST_ARB a um valor não zero para obter um PBuffer tão grande quanto possível.
Criando o PBuffer • Conseguir o dispositivo de contexto para o PBuffer hpbufdc = wglGetPBufferDCARB( hbuf ); • Conseguir um contexto de renderizado para o PBbuffer: • Criar um novo • PBbuffer consegue seu próprio estado GL: hpbufglrc = wglCreateContext( hpbufdc ); • Consultar as dimensões atuais do PBbuffer criado wglQueryPBufferARB( hbuf, WGL_PBUFFER_WIDTH_ARB, width ); wglQueryPBufferARB( hbuf,WGL_PBUFFER_HEIGHT_ARB, height );
Renderizar na textura • Pode ser feito a qualquer hora após de que a criação do PBuffer for completada • InitGL, DisplayGL, IdleGL, etc. • Deve-se selecionar o contexto de renderizado para o PBuffer corrente usando wglMakeCurrent: wglMakeCurrent( hpbufdc, hpbufglrc ); • Para voltar ao contexto da janela OpenGL wglMakeCurrent( hwindc, hwinglrc );
Renderizar na textura • O mecanismo para renderizar textura permite renderizar em regiões especificas de uma textura: • Um nível especifico de uma textura mipmapped • Uma face especifica de um textura cube map • Um especifico nível mip de uma face especifica de uma textura cube map • Pode-se usar wglSetPBufferAttribARB() para escolher qual face do cube map ou nível do mipmap para renderizar. BOOL wglSetPBufferAttribARB (HPBufferARB hPBuffer, const int *piAttribList);
Ligando o PBuffer ao objeto textura • Depois de ligar o objeto textura … • Executamos wglBindTexImageARB para ligar o PBuffer ao objeto textura. BOOL wglBindTexImageARB ( HPBufferARB hPBuffer, int iBuffer ); Ajutar “iBuffer” para WGL_FRONT_LEFT_ARB ou WGL_BACK_LEFT_ARB dependendo de qual buffer foi usado para renderizar a textura
Desligando o PBuffer do objeto texture • ***Deve-se desligar (liberar) o PBuffer da textura antes de tentar renderizar novamente na janela OpenGL*** • Executamos wglReleaseTexImageARB para desligar o PBbuffer da textura. BOOL wglReleaseTexImageARB ( HPBufferARB hPBuffer, int iBuffer)
Liberar tudo • Quando terminarmos de usar renderizado em textura, é importante garantir a liberação dos recursos consumidos pelo PBuffer. • Passos do processo de liberação • Apagar o contexto de rederizado • Liberar o dispositivo de contexto do PBuffer • Destruir o PBuffer wglDeleteContext( hpbufglrc ); wglReleasePBufferDCARB( hbuf, hpbufdc ); wglDestroyPBufferARB( hbuf );
Texturas não restritas a potencia de 2 “Non-Power-of-Two Textures” (NPTT) • OpenGL só suporta texturas com resolução 2m x 2n • Mas texturas NPTT podem ser usadas • Adequar a alguma resolução de tela ou limite que não necessariamente seja potencia de 2 (800x600) • Restrição elevada:windows/NVIDIA Extensão WGL_NV_render_texture_rectangle • NPTT • Coordenadas de textura direfentes • Âmbito s,t : [0,Width], [0,Height] respectivamente em vez do âmbito [0,1], [0,1]. • Sem filtro mipmap • Não suporta texels sem borda ou textura com modos de repeatição
Texturas não restritas a potencia de 2 “Non-Power-of-Two Textures” (NPTT) • Durante o processo de criação do PBuffer, para usar uma textura retangular de renderizado, deve-se especificar: WGL_BIND_TO_TEXTURE_RECTANGLE_RGB[A]_NV como TRUE na hora de escolher os atributos do formato de pixel e WGL_TEXTURE_TARGET_ARB como WGL_TEXTURE_RECTANGLE_NV na hora de criar o PBbuffer
Múltiplas texturas de partículas • Problema: Não se pode mudar texturas enquanto se esta desenhando uma seqüência de partículas • Após de renderizar a textura de posições deve-se atualizar as texturas de posição atual e anterior para a proxima iteração.
Resultados • Existe diversas incompatibilidades entre extenções de OpenGL: • De windows para linux • De NVIDIA para ATI • Os experimentos foram feitos numa ATI 9800 no windows usando GLUT • Para aproveitar o GPU foi usado renderizado em textura
Resultados • O ajuste para selecionar um formato de pixel na ATI é: GLint pixelFormat; const int standardIAttribList[]={ WGL_RED_BITS_ARB, 16, WGL_GREEN_BITS_ARB, 16, WGL_BLUE_BITS_ARB, 16, WGL_ALPHA_BITS_ARB, 16, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_FLOAT_ATI, WGL_STENCIL_BITS_ARB,8, WGL_DEPTH_BITS_ARB,24, WGL_DRAW_TO_PBUFFER_ARB, GL_TRUE, WGL_BIND_TO_TEXTURE_RGBA_ARB, GL_TRUE, 0}; wglChoosePixelFormatARB(hCurrentDC,standardIAttribList, fAttribList, 1, &pixelFormat, &numFormats)
Resultados • Tendo o formato de pixel podemos criar um PBuffer com os seguintes atributos: int PBufferFlags[]={ WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_2D_ARB, WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGBA_ARB, WGL_MIPMAP_TEXTURE_ARB, GL_TRUE, WGL_PBUFFER_LARGEST_ARB, GL_TRUE, 0}; wglCreatePbufferARB(hCurrentDC, pixelFormat, width, height, PBufferFlags);
Resultados • Para a simulação de partículas foi usado o integrador de Verlet, então três são texturas • O vertex shader void main(void) { texcoord = vec2(gl_Vertex); gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = ftransform(); } • O fragment-shader void main(void) { vec2 pos = gl_FragCoord.xy; vec3 oldPos = texture2D(oldPositionsTexture, getCoord(int(pos.x),int(pos.y))).rgb; vec3 currPos = texture2D(currentPositionsTexture, getCoord(int(pos.x),int(pos.y))).rgb; vec3 position = 2.0*currPos - oldPos +gravity*timestep*timestep; position = insideWorld(position); gl_FragColor = vec4(position, 1.0); }