
Assembler é minha língua favorita ... mas a vida é tão curta.
Continuo o ciclo de pesquisa sobre a questão das sombras adequadas para alguns bagels. Após a publicação, esfriei
uma e
duas vezes para este tópico, mas o efeito de ação incompleta me leva a retornar aos restos de pixels e a concluir a
gestalt .
Conhecendo a mim mesmo, tenho certeza de que o jogo dificilmente será concretizado, mas talvez o público esteja interessado nas minhas realizações nesse caminho espinhoso. E então vamos começar.
No final do último ciclo, cheguei ao entendimento de que o cálculo de gráficos em uma CPU já era o século passado, mas a obstinação natural insistia: nem todas as possibilidades eram usadas, ainda havia opções para soluções interessantes.
O traçado de raios permaneceu não implementado. Mais precisamente, seu tipo, onde, para cada pixel da imagem (bloco de pixels), um raio é lançado e o nível de iluminação do ponto atual é determinado. O algoritmo em si é descrito em um artigo anterior e não faz sentido retornar a ele. Para rastreamento de raio reverso, o código foi ainda mais simplificado, toda a trigonometria foi completamente removida, o que no futuro poderia dar um resultado aceitável.
Infelizmente, o resultado foi muito pior do que o esperado, valeu a pena implantar a imagem em tela cheia, o FPS procurou as unidades.

O agrupamento de pixels em macroblocos para reduzir os cálculos e a aplicação da suavização subsequente não melhoraram muito o desempenho. O efeito francamente não gostou da palavra.

O algoritmo era perfeitamente paralelo, mas não fazia sentido usar muitos fluxos, o efeito parecia muito pior do que no artigo anterior, mesmo com melhor qualidade de imagem.
Acabou sendo um beco sem saída. Eu tive que admitir, a CPU no cálculo de gráficos nos meus olhos se esgotou. A cortina.
Digressão 1Na última década, praticamente não houve progresso no desenvolvimento de processadores de uso geral. Se abordado pelo usuário, o aumento máximo perceptível no desempenho não será superior a 30% por núcleo. O progresso, para dizer o mínimo, é insignificante. Se omitirmos a extensão do comprimento das instruções do vetor e alguma aceleração dos blocos transportadores, isso aumentará o número de núcleos de trabalho. O trabalho seguro com threads ainda é um prazer e nem todas as tarefas podem ser paralelizadas com êxito. Eu gostaria de ter um núcleo de trabalho, embora um, mas se for, é de 5 a 10 mais rápido, mas infelizmente e ah, como eles dizem.
Aqui em Habré, há uma excelente série de artigos
"A vida na era do silício" escuro "", que explica alguns dos pré-requisitos para o estado atual das coisas, mas também retorna do céu para a terra. Na próxima década, você não pode esperar um aumento significativo na computação por núcleo. Mas podemos esperar um maior desenvolvimento do número de núcleos de GPU e sua aceleração geral. Mesmo no meu laptop antigo, o desempenho total estimado da GPU é 20 vezes maior que um único thread da CPU. Mesmo se você carregar efetivamente todos os quatro núcleos do processador, é muito menos do que gostaríamos.
Presto homenagem aos desenvolvedores dos gráficos do passado, que fizeram suas obras sem aceleradores de hardware, verdadeiros mestres.
Então, lidamos com a GPU. Acabou sendo um pouco inesperado para mim que, na prática real, poucas pessoas simplesmente espalhem polígonos em forma. Todas as coisas interessantes são criadas usando
shaders . Tendo descartado os mecanismos 3D acabados, tentei estudar as miudezas da tecnologia em um nível profundo. Os mesmos processadores são o mesmo montador, apenas algumas instruções truncadas e suas próprias especificidades de trabalho. Para o teste, parei no
GLSL , uma sintaxe do tipo C, simplicidade, muitas lições e exemplos de treinamento, incluindo o Habr.
Como eu costumava escrever em
Pascal , a tarefa era como conectar o OpenGL
para o projeto. Consegui encontrar duas maneiras de conectar: a biblioteca
GLFW e o arquivo de cabeçalho
dglOpenGL . A única coisa no começo não consegui conectar os shaders, mas aparentemente isso é pela curvatura das minhas mãos.
Digressão 2Muitos amigos me perguntam por que escrevo em Pascal? Obviamente, esta é uma linguagem ameaçada, sua comunidade está caindo constantemente, quase não há desenvolvimento. Os engenheiros de sistema de baixo nível preferem C e Java, Python, Ruby ou o que estiver no auge no momento.
Para mim, Pascal é semelhante ao primeiro amor. Duas décadas atrás, nos dias de
Turbo Pascal 5.5 , ele afundou em minha alma e tem andado comigo desde então, seja Delphi ou nos últimos anos
Lázaro . Eu gosto da previsibilidade da linguagem, do nível relativamente baixo (o assembler insere e visualiza as instruções do processador), da compatibilidade com C. O principal é que o código é montado e executado sem problemas, mas o fato de não estar na moda está desatualizado e não há alguns recursos, isso não faz sentido. Dizem que existem pessoas que ainda escrevem no
LISP , mas para ele em geral há meio século.
Então, vamos mergulhar no desenvolvimento. Para uma etapa de teste, não tomaremos modelos realistas precisos de sombreamento, mas tentaremos implementar o que já tentamos antes, mas com o desempenho da GPU, por assim dizer, para uma comparação clara.
Inicialmente, pensei em obter uma sombra aproximadamente dessa forma, usando triângulos para um objeto.

Para criar o efeito de um círculo suave, você precisa de muitos polígonos. Mas e se você usar triângulos no mínimo, usando um pixel shader para criar um buraco na forma. A ideia surgiu depois de ler um
artigo de um mestre respeitado, no qual foi aberta a oportunidade de criar uma esfera com um sombreador.

Se você estender o triângulo além dos limites da tela, o resultado será o seguinte:

As bordas da sombra se mostraram muito rígidas e também pisaram. Mas há uma maneira de obter um resultado aceitável sem usar
superamostragem , isso é usar bordas suavizadas. Para fazer isso, altere ligeiramente o esquema. Os cantos dos polígonos na interseção da tangente ao círculo ficarão transparentes.

O resultado é melhor, mas ainda parece antinatural.

Adicione um pouco de suavização do círculo para dar suavidade e também mude a forma do gradiente de linear para poder.

É um resultado aceitável.
E no final, adicionaremos objetos imitando obstáculos ao formulário.

Código de sombreador//
#version 330 core
layout (location = 0) in vec2 aVertexPosition;
void main(void) {
gl_Position = vec4(aVertexPosition.xy, 0, 1.0);
}
//
#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 5) out;
uniform mat4 uModelViewMatrix;
uniform float uRadius;
uniform vec2 uHeroPoint;
out float fTransparency;
out vec2 vCenter;
void main(){
vCenter = gl_in[0].gl_Position.xy;
vec2 d = uHeroPoint - vCenter;
float l = length(d);
float i = uRadius / l;
float ii = i*i;
float ij = i * sqrt(1 - ii);
vec2 p1 = vec2(vCenter.x + dx*ii - dy*ij , vCenter.y + dx*ij + dy*ii);
vec2 p2 = vec2(vCenter.x + dx*ii + dy*ij , vCenter.y - dx*ij + dy*ii);
d = uHeroPoint - p1;
vec2 p3 = vec2(p1 - d/length(d)*1000000);
d = uHeroPoint - p2;
vec2 p4 = vec2(p2 - d/length(d)*1000000);
fTransparency = 0;
gl_Position = uModelViewMatrix * vec4(p1, 0, 1);
EmitVertex();
fTransparency = 1;
gl_Position = uModelViewMatrix * vec4(p3, 0, 1);
EmitVertex();
gl_Position = uModelViewMatrix * vec4(vCenter, 0, 1);
EmitVertex();
gl_Position = uModelViewMatrix * vec4(p4, 0, 1);
EmitVertex();
fTransparency = 0;
gl_Position = uModelViewMatrix * vec4(p2, 0, 1);
EmitVertex();
EndPrimitive();
}
//
#version 330 core
precision mediump float;
varying float fTransparency;
varying vec2 vCenter;
uniform float uRadius;
uniform vec2 uScreenHalfSize;
uniform float uShadowTransparency;
uniform float uShadowSmoothness;
out vec4 FragColor;
void main(){
float l = distance(vec2((gl_FragCoord.xy - uScreenHalfSize.xy)/uScreenHalfSize.y), vCenter.xy);
if (l<uRadius) {discard;}
else {FragColor = vec4(0, 0, 0, min(pow(fTransparency, uShadowSmoothness), (l-uRadius)/uRadius*10)*uShadowTransparency);}
}
Espero que tenha sido informativo
Seu humilde servo, atormentador de pixels, Rebuilder.
Estou anexando uma pequena
demonstração . (EXE Windows)
PS O título do artigo contém um
ovo de Páscoa , uma referência à trilogia
Siala Chronicle . Um excelente trabalho nos estilos de fantasia, sobre os infortúnios dos chifres, de Alexei Pekhov.