terça-feira, 5 de fevereiro de 2019

Windows 10 Visual Studio Code Setup with C++, CUDA, and "CUDA by Example" book



This guide will teach you how to set up Visual Studio Code to work with CUDA like a Linux machine(as in, compiling using the VS Code terminal without any over-complicated bs). CUDA needs the Microsoft Visual compilers to work, but we don't need to actually donwload and install Visual Studio Community for that. Let's see how!

First things first:
  1. Install VS Code;
  2. Install Cuda Toolkit from Nvidia;
  3. Install MinGW by select every basic component(gcc stuff) on the installer;
  4. Install Build Tools for Visual Studio (this is a package that contains the VS compilers needed to compile CUDA code, more specifically the cl.exe that nvcc loves so much)
Now, set up the PATH in the System Variables(I will use my own file paths, you should know yours. These are all the default locations):
  1. C:\MinGW\bin
  2. C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\bin
  3. C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64
Time to see if everything up until now is cool. Run cmd and type "g++ -v" and "nvcc -V". They should output the version for each compiler. If you followed every step until now and it didn't work, restart cmd; it needs to be freshly run after modifying the system PATH. If it still doesn't work, may God help you.

Reminding that VS Code can be set up to use the good old cmd instead of the Powershell, if you wish. VS Code will ask you if you want to change it the first time you open the terminal.

Now, to make the "Cuda by Example" code work, we need to copy the glut files on the bin and lib folders to the right places. The .dll files go into the system32 folder, and the lib files to into you CUDA installation lib subfolder(mine is C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\lib\x64, because I'm on a 64-bit OS).

That should do it. Now you can code kinda of like on Linux, using terminal commands.

sexta-feira, 2 de junho de 2017

Resultados Finais de PBR

Eis aqui alguns resultados da implementação dos materiais espelho, vidro, e cook-torrance:




E, finalmente, a cena final:


sexta-feira, 14 de abril de 2017

[Renderizando Título]

Depois de programar o Ray Tracer para que ele pegue a informação e cor de onde o raio bate, vamos aplicar a verdadeira, legítima equação de rendring  para gerarmos imagens muito mais realistas, já que vamos considerar a refletância e a emitância, assim a iluminação virá naturalmente. No fim, teremos uma cena gerada simulando os "raios" de luz da vida real.

A primeira etapa é programar o pixel sampling, que significa atirar vários raios de um mesmo pixel, com cada raio saindo com direções levemente diferentes entre si. O tipo de pixel sampling utilizado foi o  uniforme: cada raios sai com um offset aleatório máximo de ±0.5 pixel em x e y em relação ao centro do pixel. Com isso, a imagem gerada na versão anterior já vem mais bela pois o pixel sampling reduz o aliasing:

1 sample por pixel(utilizando offset aleatorio) vs. vários samples por pixel. Número de samples perdidos no tempo. Desconsidere a compressão sebosa.
Mas isso é apenas o primeiro passo. Antes de continuar vamos dar uma investigada na equação de rendering:
Fonte: slides do Prof. Christian
A equação nos diz que a radiância(energia luminosa) de um raio de luz que sai de uma pequena superfície(representado na equação como Lo) é igual à soma da radiância emitida(Le) por essa mesma superfície mais o somatório de todas as radiâncias dos raios de luz que chegam nessa superfície(Li). 

A radiância emitida, ou emitância, pode ser entendida como a quantidade de luz que a superfície emite. Uma superfície que emite luz poderia ser, por exemplo, o bulbo de uma lâmpada acesa ou o Sol. A maioria das superfícies não emite nenhum tipo de luz, por isso as coisas são pretas no escuro(quando não há nenhuma fonte de luz que gere "raios" que podem refletir de superfícies).

A radiância incidente é a radiância que vem de outras direções(Ω): pode ser diretamente de uma fonte de luz ou pode ter sido refletida diversas vezes até chegar no ponto em questão.

No entanto, a radiância incidente depende também do material da superfície(como este reflete/absorve a luz) e também do ângulo de incidência do raio(θ, quanto mais perpendicular o raio de luz da superfície, menor fluxo radiante, e assim, menor a radiância).

O material da superfície é definido e representado pelo fr(ωi, ωr), que é uma função de descreme como raios vindos de uma direção ωi é refletido para uma direção ωr. Cada material possui uma função fr(), chamada mais popularmente de BRDF. É uma descrição matemática de como o material reflete luz.

O cálculo dessa integral é feito através do método de Monte Carlo, um método que utiliza probabilidades. Na nossa implementação, só tomamos um raio incidente por parte de superficie, o que facilita as contas e implementação.

Como o Ray Tracer funciona jogando raios da câmera, é mais simples ter apenas um raio incidente por superfície. Quando um raios é jogado, calcula-se o Lo pegando a emitância do material de onde o raio interseccionou e somamos com a radiância do raio incidente. Já que o Ray Tracer funciona "ao contrário", geramos um raio saindo desse ponto de interseção a uma direção aleatória e pegamos o Lo dele. Notamos que aqui já entramos em recursão. Para não simularmos os raios refletindo infinitamente(e em consequência rodarmos um programa sem fim) definimos um número máximo de vezes(max depth) que um raio saindo da câmera pode refletir nas superfícies. Cada vez que um raio é refletido, há uma possibilidade que ele chegue à fonte de luz, então aquele ponto é iluminado e suas informações de cor são calculadas através de sua BRDF. Se um raios refletir o numero máximo de vezes e não chegar em uma fonte de luz, a radiância daquele raio será nula.

É nesse ponto que vemos a importância do pixel sampling: A chance de um raio chegar em uma fonte de luz pode ser baixa, então vários raios de luz são jogado por pixel, e no final, quando todos os raios já tiverem terminado de refletir na cena, a cor do pixel será uma média das radiâncias de cada raio. Quanto mais samples por pixel, mais provável será que algum raio daquele pixel chegará na luz, gerando uma imagem proporcionalmente mais realista.

O algoritmo, em pseudo linguagem, da equação de rendering, retirados dos slides do Prof. Christian:

Essa parte joga os raios da câmera já com o offset.

Essa é a parte recursiva: o raio é gerado e o Lo é somado. O 2*PI é multiplicado pois faz parte do integrador de Monte Carlo.
Utilizando como cena uma caixa estilo Cornell box, geramos as seguintes imagens, com 64, 256 e 2048 samples por pixel, respectivamente. Todas elas tem um max depth 5.

 
🔝🔝🔝🔝🔝

Tivemos alguns problemas misteriosos que no final das contas eram uma má implementação da câmera e dos algoritmos dos triângulos. Resolvidos estes, o programa funcionou com toda sua elegância.

Para melhorar os resultados, além de aumentar o número de samples por pixel e o max depth, podemos implementar roleta russa, e para facilitar a configuração de cena, implementar a importação de objetos já com as informações de refletância e emitância.

Álbuns da vez(esse foi longo)


sexta-feira, 3 de março de 2017

Triângulo, Câmera, Macaco!

Com o Template de Ray Tracer do professor Christian e o Capítulo 10 do Realistic Ray Tracing de Peter Shirley em mãos, podemos implementar a primitiva Triângulo e o teste de interseção Raio-Triângulo. O código ficou praticamente igual ao do livro, e com um simples teste, observamos que o algoritmo do livro funcionou perfeitamente: 

Pirâmide de Giza em construção, circa 2570 BC

O próximo passo é implementar uma câmera com distorção perspectiva, ou Pinhole Camera, para podermos gerar imagens realistas, já que é com distorção perspectiva que observamos o mundo. Seguindo os slides do professor, foi fácil implementar essa câmera, inicialmente na origem. Além disso, torná-la arbitrária não requiriu nenhuma programação extra, sendo apenas necessário modificar os parâmetros na hora de criar o objeto câmera. Observe:

Ângulo alternativo









Uma pequena mudança que fizemos foi mudar a logica da cor do Template do Ray Tracer original: agora as coisas perto da tela ficam mais claras, e não o contrário. Mais intuitivo!

A possibilidade de renderizar triângulos utilizando uma câmera perspectiva abre as portas para o objetivo supremo destas atividades: Renderizar Suzanne, a famigerada prim(ata)itiva do Blender. Com ajuda da biblioteca Assimp, podemos importar as informações de vértices do modelo no formato .obj do símio e renderizar seus triângulos do jeito que devem ser. Admire:


Um detalhe: originalmente Suzanne é feita de quadriláteros, mas o Assimp, ao importar o arquivo do modelo, transforma os quadriláteros em triângulos, já que estamos trabalhamos com essa primitiva.

O último passo para a perfeição da atividade é atribuir cores aleatórias para cada triângulo. Para fazer isso, adicionamos um atribulo de cor, em RGB, na primitiva triângulo e fizemos a função de interseção retornar a cor da primitiva ao invés da distância. Aprecie:


Uma coisa que notei ao setar a câmera para ângulos bons é que meu código parece estar fora de "escala" em comparação aos programas dos colegas. Algo que deve ser investigado, mas não deve ser nada além de encheção de saco na hora de setar a câmera.

Álbum da vez:

quinta-feira, 2 de fevereiro de 2017

Mergulhando no Mitsuba

As postagens daqui em diante serão sobre a nova disciplina do professor Christian, Physically-Based Rendering(PBR). Este post é sobre a primeira atividade da disciplina: criar uma cena legal, renderizá-la usando um programa de PBR e publicá-la no blog da dupla, que é este aqui. A dupla sou eu e Rafael "Mala" Nunes. Cada um de nós gerou uma cena final utilizando o Mitsuba, escolhido dentre outros softwares, por sugestão do professor. Achamos ele fácil de utilizar, principalmente por ser bem documentado e tendo recebido dicas e sugestões de outros colegas da disciplina. 

A primeira cena, criada por mim, foi feita utilizando 8 esferas de diferentes materiais, um retângulo texturizado que serve como base, um environment map que embeleza e ilumina a cena e um modelo de um dragão chinês que aparentemente é um importante símbolo da cultura chinesa e não deve ser zoado. O dragão é o único objeto da cena que recebeu a característica de subsurface scattering, que simula a absorção dos raios de luz pelo objeto e espalha-os sobre camadas mais interiores. Essa característica requer cálculos extras antes da renderização em si, atrasando a imagem final, mas dá um efeito muito belo.

[Música-tema do Dragon Ball]
A segunda cena, criada por Rafael, é baseada em um exemplo do Mitsuba. Aqui, as fontes de luz são duas: a lâmpada no teto e o cubo na mesa. Desta vez, temos mais objetos importados para a cena, ao invés de usar objetos gerados pelo Mitsuba. Esta cena exalta objetos de vidro e materiais polidos, além de fazer referências sutis a clássicos do Thrash Metal americano. Você consegue identificá-las? 

Olhe com cuidado...
Estes foram os resultados das atividades. Aprendemos a utilizar o Mitsuba e a projetar uma cena utilizando a sintaxe de arquivos XML, que é chata. Vamos utilizar os conhecimentos adquiridos aqui para nos auxiliar em testes futuros com nossos próprias renderizadores. Nos vemos na semana que vem, eu espero!

Álbum da vez:

 

segunda-feira, 22 de agosto de 2016

Abrindo os portões do inferno do GLUT

Hoje eu resolvi dar uma olhada nos tutoriais de http://nehe.gamedev.net/ para começar a brincar com OpenGL. Baixei o Visual Studio(Community Edition, creio eu) mas os tutoriais de lá usam uma tal tecnologia paleolítica chamada GLAWX, que não é suportada mais hoje. 

Depois de quase trocar de curso, encontrei uma página que explica que existe um tal de nuopengl, que aparentemente inclui todos os gluts, freegluts, glawx, a porra toda, é bem fácil de instalar. Só tome cuidado com o código exemplo do site, a função glutinit deveria ser iniciada glutInit(&argc, argv); 

Amanhã eu tento fuçar mais um pouco no OpenGL. Fiquem ligados para o próximo episódio de "Erro de Compilação"!
Álbum da vez:

sexta-feira, 12 de agosto de 2016

Interpolação de cores e Triângulos

Finalmente, vamos falar sobre a interpolação linear de cores em linhas e a função de rasterizar triângulos, que na verdade nada mais é que uma função que rasteriza três linhas em ordem.

A interpolação de cores em uma linha significa começar a rasterização da linha utilizando uma cor e, pouco a pouco, modificar essa cor para que no fim da rasterização da linha tenhamos outra cor. É como um degradê, basicamente.

Para isso, precisamos primeiramente passar para a função de rasterizar linhas nossa cor inicial e final. Utilizaremos as cores que definimos anteriormente por praticidade, mas podemos, sem problema nenhum, utilizar qualquer cor do sistema RGBA.

Já que o algoritmo de rasterização funciona em etapas de pixel por pixel, só podemos modificar nossa cor a cada pixel, ou ciclo, do algoritmo. Então, temos que definir qual será nosso incremento de cor para cada canal de cor da linha por ciclo de rasterização. O cálculo do incremento do canal vermelho, por exemplo, é dado por:

dr = (color1.r = color0.r)/dx ou dy

onde dr é o incremento do canal vermelho, color1.r é o valor do canal vermelho no fim da linha, color0.r é o valor do canal vermelho no início da linha, e dx ou dy refere-se ao comprimento da linha projetada no eixo com o qual a função de rasterização trabalha. dx ou dy também é o número de vezes que o algoritmo de rasterização roda. Agora entendemos como e porque utilizamos este valor de dr. Para os outros canais de cores, a definição do incremento é análogo.

No final de cada ciclo do algoritmo, a cor que está sendo utilizada para o pixel atual recebe os respectivos incrementos, aproximando pouco a pouco da cor final. Abaixo temos o código da função de rasterização no primeiro quadrante com as modificações para a interpolação de cores:

void DrawLine1O(int x0, int y0, int x1, int y1, cor color0, cor color1){

    int dx = x1 - x0;
    int dy = y1 - y0;

    cor color = color0;

    float dr = (color1.r - color0.r)/dx;
    float dg = (color1.g - color0.g)/dx;
    float db = (color1.b - color0.b)/dx;

    int d = 2*dy - dx; //valor inicial de d

    int incE = 2*dy;            //valores iniciais para incrementos E e NE
    int incNE = 2*(dy - dx);

    int x = x0; //valores iniciais para rasterizacao
    int y = y0;

    PutPixel(x, y, color); // bota primeiro pixel

    while(x < x1){
        if(d<=0){
            d += incE;
            x++;
        }else{
            d +=incNE;
            x++;
            y++;
        }

        color.r = color.r+dr; // incrementa cor por pixel  ate a cor final no ultimo pixel
        color.g = color.g+dg;
        color.b = color.b+db;

        PutPixel(x, y, color);
    }
}


Inicialmente, quando utilizava os canais de cores como tipo inteiro, a interpolação não funcionava corretamente. Se eu tentasse rasterizar uma linha que começava azul e terminava vermelha, por exemplo, a cor azul ia aos poucos esmanhecendo enquanto o canal vermelho não era incrementado corretamente. Trocando os tipos dos canais para float, o problema foi corrigido. Outros bugs psicodélicos ocorreram, mas somente quando tentava utilizar outro cálculo de incremento.

Linhas com interpolação de cores!
Por último, vamos entender como funciona a rasterização de triângulos. A função recebe três coordenadas(os vértices do triângulo) e três cores, uma para cada vértice. Aí são chamadas as funções de rasterização de linha: do ponto 1 ao ponto 2, começando com a cor 1 e terminando com a cor 2;  do ponto 2 ao ponto 3, começando com a cor 2 e terminando com a cor 3; e do ponto 3 ao ponto 1, começando com a cor 3 e terminando com a cor 1. Dessa forma, as cores sempre se unem nas vértices. É nesse momento que vemos outra vantagem de utilizar funções de rasterização "de trás para frente", que é essa coincidência de cores nos vértices.

Três (3) triângulos rasterizados!

Concluindo, o projeto foi um sucesso. Os conceitos foram entendidos completamente e implementations em código. Mal posso esperar para aprender mais sobre OpenGL. PS: Este programa contém GLUT.

REFERENCIAS:
Slides do professor Christian
http://stackoverflow.com/questions/25878029/smooth-color-interpolation-along-a-bresenham-line
http://stackoverflow.com/questions/13488957/interpolate-from-one-color-to-another

Álbum da vez: