Dúvida de OpenGL


#1

Gostaríamos de saber se alguém que entende de OpenGL pode resolver nossa dúvida:

No nosso jogo Horus Eye, estamos tentando migrar a framework de puro SDL pra SDL+OpenGL. Já estã praticamente tudo feito. O que faltou foi o nosso sistema de FOG.

Esse sistema é bem semelhante ao de jogos como Diablo I & II e Tibia. O cenário (2D isométrico) é escuro mas pode ser iluminado por focos de luz. Na versão de puro SDL nós conseguimos fazer isso renderizando uma imagem preta mas com vazados de forma elíptica que ficavam menos opacos (mais transparentes) nos pixels mais próximos aos centros das elipses. Basicamente, o jogo era visto através de buracos de forma elíptica com degradê, o que dá a impressão de luz nas elipses e sombra longe delas.

Isso nos custava muita eficiência e foi um dos principais motivos por optarmos pela mudança para OpenGL. Só que não encontramos um jeito de obter o mesmo resultado com algum ganho efetivo de velocidade. Existe alguma funcionalidade do OpenGL que faça facilmente o que queremos?

Por enquanto estamos explorando meios alternativos de fazer a FOG bem diferentes do modelo em SDL.

Agradecemos a atenção!

Wil


#2

Bom existem várias formas de resolver o problema proposto utilizando o opengl

1 - Utilizar uma técnica chamada multipass rendering. Primeiro renderiza-se a cena sem qualquer efeito de fog, em seguida, utilizando o GL_BLEND, renderiza-se o fog por cima da cena que jah está no framebuffer. O fog pode ser pré calculado em uma textura e renderizado utilizando-se quads texturizados (É claro que nada impede que o fog seja renderizado antes da cena).

2 - Outra alternativa um pouco mais avancada seria utilizar as funcionalidades do fragment shader para calcular os efeitos de iluminacao por fragmento (um fragmento é um pixel com algumas informacoes a mais).

Vale lembrar que para utilizar a alternativa 2 tanto a placa de vídeo quanto o driver de vídeo devem suportar o uso do glsl, então seu código deve em tempo de execucao verificar se a versão do opengl do usuario é >= 2.0. (Ou se a placa possui extensões que suportam o glsl).


#3

1 - É o que fazemos com o SDL, que tem Surfaces (bitmaps) e a funcionalidade Blit. Nós pré-calculamos a surface da FOG (na força bruta i.e. pixel por pixel dados os focos de luz) e blitamos ela em cima da cena do jogo. Agora com OpenGL não conhecemos um meio prático de substituir as Surfaces. Sabemos que o OpenGL tem texturas e é o que usamos, mas não temos o mesmo controle sobre elas quanto temos sobre as Surfaces. Não podemos (ou sabemos como) mexer nos pixels dela. Podemos continuar fazendo as contas numa Surface e dps passar para uma textura, mas pelo que testamos fica custoso carregar uma textura a cada frame do jogo. Tem algum jeito de mexermos diretamente na textura?

2 - É o que esperamos conseguir fazer a longo prazo. Mas por enquanto não temos tempo pra aprender devidamente a usar Shaders… Temos que apresentar o jogo na metade de Outubro. Embora seria muito bom futuramente termos domínio sobre esse tipo de recurso!

Na última reunião decidimos que ficaríamos com a versão implementada em SDL, que apesar de rodar em apenas 30~40 fps na maioria do computadores, não deixa de ser visualmente apresentável. Mesmo assim, insisto em aprendermos mais OpenGL porque seria muito bom conseguirmos aplicá-lo no jogo mesmo que seja depois da apresentação.

Valeu pela ajuda!


#4

Bom Kazuo existem algumas diferenças fundamentais entre a abordagem de vcs com o sdl e a abordagem proposta utilizando o opengl.

Na abordagem do SDL, vcs eram obrigados a fazer o processamento diretamente na CPU, enquanto no openGL, o blending eh feito em um dos estágios finais do pipeline, ainda na GPU, ou seja, dependendo da placa de vídeo do usuário o opengl poderia estar atualizando todo o framebuffer de forma concorrente.

Eu vou deixar aki um snippet demonstrando o uso do blend: glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glEnable(GL_BLEND); // Seja s = (sr, sb, sg, sa) a cor que será colocada no pixel. // e d = (dr, db, dg, da) a cor que jah estava lah. // A cor que ficará no pixel será: cor = 0 * s + sa * d. glBlendFunc(GL_ZERO, GL_SRC_ALPHA); // Desenha um quad que irá mascarar a cena através do canal alpha glBegin(GL_QUADS); glVertex3f(-1,1,0); glColor4f(0,0,0,1); glVertex3f(1,1,0); glColor4f(0,0,0,0); glVertex3f(1,-1,0); glColor4f(0,0,0,0); glVertex3f(-1,-1,0); glColor4f(0,0,0,1); glEnd(); // cor = da * s + 0 * d glBlendFunc(GL_DST_ALPHA, GL_ZERO); glColor4f(1,0,0,1); // Desenha qualquer coisa glutSolidSphere(1,10,10); glDisable(GL_BLEND); // pode trocar por SDL_GL_SwapBuffers() glutSwapBuffers();
No seu caso vc terá uma situação um pouco mais complexa do que a tratada pelo snippet, vc terá que usar um código parecido com o abaixo:

Limpar a cena com preto e zero no alpha glEnable(GL_BLEND) glBlendFunc(GL_ONE, GL_ONE) para cada objeto que emite luz: desenha um quad com a máscara glBlendFunc(GL_DST_ALPHA, GL_ZERO) desenha toda a cena com cores glDisable(GL_BLEND)

Acho que com isso vcs jah devem conseguir algo mais legal.