Implementando Raytracing usando XNA


Este post irá falar um pouco sobre um renderizador baseado em raytracing que eu implementei um tempo atrás usando XNA 3.1 e C#.

Iniciarei com uma introducão teória (fácil de acompanhar) e em seguida mostrarei alguns detalhes da implementação.

Introdução Teórica

A renderização por raytracing é bastante diferente da baseada em rasterizacão (veja este post http://ploobs.com.br/?p=622 para entender um pouco mais sobre rasterizacão ).

O algoritmo mais classico utilizado em raytracing, baseia-se na simulação do trajeto que os raios de luz percorreriam no mundo real, porém ao invés do raio sair da luz e chegar aos objetos (e eventualmente até a câmera), o nosso raio sairá da câmera e irá em direcão aos objetos. (fisicamente correto segundo as leias da ótica)

No mundo real, os raios de luz são emitidos a partir de uma fonte de luz, percorrendo o espaço até encontrar um objeto. Após os raios de luz atingirem o objeto, estes são refratados ou refletidos, de acordo com as características do objeto, nomeadamente, cor, textura e transparência, alterando assim a sua trajetória, e fazendo com que apenas uma infinitésima minoria dos raios que partiram da fonte de luz atinjam, por fim, os olhos do observador.

Como se pode perceber facilmente, implementar computacionalmente um sistema que simulasse o que foi descrito no parágrafo anterior é impraticável e um tanto quanto desnecessário (seria um desperdício já que a quantidade de raios de luz que atingem o olho do observador é extremamente pequena em comparacão com a quantidade de raios emitidos pelas luzes ). Assim sendo, o algoritmo ray tracing limita-se a inverter o sentido do trajeto dos raios de luz, passando agora o observador a ser a fonte ( apenas serão processados os raios que efetivamente são vistos pelo observador). Esta inversão de papéis tem seu custo, uma vez que não conseguimos simular com precisão a luz indireta que ilumina a cena.

Apesar da simplificacão feita, está técnica ainda é bastante pesada computacionalmente, portanto é apenas indicada para áreas como cinema, Tv, vídeos (em que precisamos de fotorrealismo e não estamos preocupados com velocidade).

Descrição do algoritmo

O algoritmo ray tracing é um algoritmo recursivo que consiste em projetar, a partir do observador (câmera), um vetor (raio) para cada um dos pixels da nossa cena/imagem, vetor este que irá interceptar os objetos que formam a cena.
O raio emitido pode:
  • Não interceptar nenhum objeto no seu trajeto, desta forma atribuimos ao pixel que o gerou a cor do background
  • No caso de o raio interceptar algum objeto, é necessário determinar a cor do pixel correspondente. Para tal é necessário calcular a iluminação no ponto da cena que o raio atinge, iluminação esta que pode ser proveniente diretamente de fontes de luz, pode ser luz proveniente de outro objeto que por reflexão ilumina o ponto que estamos a analisar, pode ser luz refratada transmitida através do objeto e que assim ilumina o ponto, ou pode ainda ser uma combinação de mais do que uma destas formas de iluminação.

Quando o raio encontra um objeto, são gerados três outros raios secundários:

Cada um destes raios possui características e objetivos diferentes. Estes raios secundários gerados irão novamente colidir com outros objetos … conforme mostra a imagem abaixo (omitidas as fontes de luz e os raios de sombra).

 

Como podemos observar na figura um único raio pode ser refletido e refratado indefinidamente, subdividindo-se em vários raios secundários, formando assim uma árvore de raios, mais ou menos, complexa, de acordo com a composição da nossa cena. Para conseguirmos processar esta informação, necessitamos de estabelecer um limite máximo de subdivisões. (normalmente estabelecemos uma profundidade máxima para esta árvore, ao alcancá-la, atribuímos a cor de background ao raio mais baixo da arvore e não o subdividimos)

Quando todos os raios chegarem aos seus destinos (seja atraves do criterio de máxima subdivisao ou por terem atingido o background), as suas contribuicões são calculadas do fim para o começo. A cada interseccão, utilizamos uma equacão (será vista mais a frente) para combinar os efeitos dos raios de sombra, reflexão e refracão. No fim teremos apenas um valor que será atribuído ao pixel que gerou o primeiro raio.

As sombras são verificadas através de raios secundários que são lançados a partir do ponto de intersecção do raio primário com o objeto (conforme visto na primeira imagem), em direção ao foco de luz. Se no seu trajeto o raio cruzar-se com um objeto, é porque o ponto em análise se encontra na sombra, se não, é porque recebe luz direta. Em miúdos =P, se tiver alguma coisa entre o ponto de interseccão e a fonte de luz, é porque o objeto está na sombra para esta luz.

O pseudo algoritmo abaixo mostra um pouco da dinâmica do sistema:

Criar um raio a partir do ponto de visão (observador) e que passe pelo PixelAtual
(Para cada objeto da cena)
  {
     Se o raio intercepta o ObjetoAtual e esta é a intersecao mais proxima
     {

      Lançar um novo raio na direcao de cada fonte de luz (raios de sombra)
       Se a superfície é reflectora
       {
          gerar um raio refletor (recursivo)
        }
        Se a superfície é transparente
       {
        gerar um raio refrator (recursivo)
       }
       Devolver a combinacao entre os valores obtidos dos raios de sombra, raio refletor e raio refrator
}
   Se Nao encontrou nenhum objeto
{
      Preencher PixelAtual com a cor do fundo
}
 }

Implementação

A implementacão feita não inclui raios refratados (se alguém quiser se aventurar =P boa sorte, não é uma tarefa difícil não)

O metodo a seguir é chamado a todo frame (removi algumas linhas sobre suporte a multiThread que não ajudavam em nada na compreensão da técnica).

Implementei um pequeno antialiasing (tente desabilitá-lo e veja como que a imagem fica horrível). Usei algo próximo do SuperSample (por pixel eu jogo diversos raios seguindo um padrão e uso a média entre eles).

public void Draw()
{
            for (int y = 0; y <  ScreenSizeY; y++)
		{
                         for (int x = 0; x < ScreenSizeX; x++)
	           	{				

					Ray tPrimaryRay = _camera.GetUnprojectedRay(x, y);
					FloatColour tColour = TraceRay(null, tPrimaryRay, 0,0);

                                       ///filtragem
                                        if (StaticContent.isAntialiasing == true)
                                       {
                                         float[] antiAliasingDistances = StaticContent.AntiAliasingDistances;
                                         Vector2[] pattern = StaticContent.Pattern;
                                         for (int i = 0; i < antiAliasingDistances.Length; i++)
                                        {
                                           for (int j = 0; j < pattern.Length; j++)
                                            {
                                                tPrimaryRay = _camera.GetUnprojectedRay(x + pattern[j].X * antiAliasingDistances[i], y + pattern[j].Y * antiAliasingDistances[i]);
                                                tColour += TraceRay(null, tPrimaryRay,0, 0);
                                             }
                                         }

                                         Image[x, y] = tColour / (antiAliasingDistances.Length * pattern.Length + 1);
                                          }
                                          else
                                          {
                                            Image[x, y] = tColour ;
                                          }
			}
		}

	}

O método que cria os raios para cada pixel na tela pode ser visto a seguir:

public Ray GetUnprojectedRay(float x, float y)
        {
            Vector3 nearSource = new Vector3(x, y, 0);
            Vector3 farSource = new Vector3(x, y, 1);
            Vector3 nearPoint = StaticContent.GraphicsDevice.Viewport.Unproject(nearSource, Projection, View, Matrix.Identity);
            Vector3 farPoint = StaticContent.GraphicsDevice.Viewport.Unproject(farSource, Projection, View, Matrix.Identity);
            Vector3 direction = Vector3.Normalize(farPoint - nearPoint);
            return new Ray(nearPoint, direction);
        }

A idéia é simples:

  1. Sabemos que este raio sai da camera e atinge os objetos
  2. Sabemos que este raio (em projecao perspectiva)  em espaco de projecão corresponde a uma reta
  3. Intuitivamente, todos os objetos que interceptam este raio, quando projetados ocuparão o mesmo pixel (tente lancar um raio do seu olha pra alguma direcao, voce percebera que voce vera apenas o primeiro interceptado, o segundo estaria projetado na mesma posicao do segundo, o terceiro idem … em um render tradicional por rasterizacao, o ZBuffer resolve quem deve ficar na frente de quem =P)
  4. Basta então criar dois pontos com o mesmo X,Y (e qualquer Z) em espaco de projecao e “desprojetá-los ” (multiplicar pela inversa da Matriz View * Projection) e teremos os pontos em espaco World, em seguida construimos um vetor entre eles e temos o nosso raio procurado =P

OBS: lembre-se que espaco de projecão normalizado, o X,Y correspondem a posicão do Pixel na tela normalizada e o Z corresponde a distancia “normalizada” dele em relacão a câmera.

A funcão a seguir mostra como calculamos a contribuicão de cada um dos raios.

public FloatColour TraceRay(Primitive pIgnorePrimitive, Ray tRay, int nDepth , float acumulatedDistance)
		{
			    if (nDepth > StaticContent.MaxDepth)
			{
				return StaticContent.BackGround;
			}

			float fClosestIntersectionDistance = float.MaxValue;
			Primitive closestIntersectedPrimitive = null;

			foreach (Primitive primitive in m_pScene.Primitives)
			{

				float fDistance;
				if (primitive.Intersect(tRay, out fDistance))
				{

					if (fDistance < fClosestIntersectionDistance)
					{

						fClosestIntersectionDistance = fDistance;
						closestIntersectedPrimitive = primitive;
					}
				}
			}

			FloatColour tColour = StaticContent.BackGround;
			if (closestIntersectedPrimitive != null)
			{
				tColour = closestIntersectedPrimitive.Material.Shade(this, tRay, fClosestIntersectionDistance,acumulatedDistance, nDepth + 1);
			}

			tColour = FloatColour.Min(tColour, new FloatColour(1));

			return tColour;
		}

Verificamos se atingimos a subdivisão máxima, em seguida procuramos a primeira interseccão, caso exista chamamos o método Shade do material, caso não existe atribuímos a cor de BackGound. (checamos também para ver se a cor não está saturada).

O metodo primitive.intersect checa se o raio intercepta a primitiva em questão, cada Primitiva (como triangle, sphere, box …) contem uma implementacao diferente. Eu não otimizei nada, usei as equacões clássicas sem estrutura de aceleracão nenhuma (Triangle Mesh DEMORA, tá BRUTAL force total). Quem estiver interessado, procure sobre KD-Tree e/ou Octres para acelerar a colisão com triangles meshes =P

O método abaixo mostra como que o Shading é feito.

public virtual FloatColour Shade(RayTracer tracer, Ray tRay, float fDistance, float totalDistance ,int nDepth)
        {
            FloatColour tColour = new FloatColour(0, 0, 0);            

            Vector3 tIntersection = tRay.Position + tRay.Direction * fDistance;
            Vector3 tNormal = prim.CalculateNormal(tIntersection);

            Ray tReflectedRay;
            tReflectedRay.Direction = Vector3.Reflect(tRay.Direction, tNormal);
            tReflectedRay.Position = tIntersection;

            // add lighting components
            foreach (Luz light in tracer.Scene.Luzes)
            {
                // get vector from surface to light
                Vector3 tSurfaceToLight = light.GetDirectionToLight(tIntersection);
                float fDistanceToLight = tSurfaceToLight.Length();
                tSurfaceToLight.X /= fDistanceToLight;
                tSurfaceToLight.Y /= fDistanceToLight;
                tSurfaceToLight.Z /= fDistanceToLight;

                Ray tRayToLight;
                tRayToLight.Position = tIntersection;
                tRayToLight.Direction = tSurfaceToLight;
                float fLightFactor = 0;
                float fSpecular = 0;

                if (tracer.IsShadowed(prim, tRayToLight, fDistanceToLight))
                {
                    fLightFactor = StaticContent.AmbientLightContribuition;
                }
                else
                {
                    fLightFactor = Vector3.Dot(tSurfaceToLight, tNormal);
                    if (fLightFactor < 0) fLightFactor = 0;
                    fSpecular = Vector3.Dot(tRayToLight.Direction, tReflectedRay.Direction);
                    if (fSpecular > 0)
                    {
                        fSpecular = (float)Math.Pow(fSpecular, Shininess) * SpecularCoefficient;
                    }
                    else
                    {
                        fSpecular = 0;
                    }
                }
               tColour += (getColor(tIntersection) * fLightFactor + new FloatColour(fSpecular)) * light.Color * light.GetIntensity(tIntersection);

            }

            if (Reflectivity != 0)
            {
                FloatColour tReflectedColour = tracer.TraceRay(prim, tReflectedRay, nDepth + 1, totalDistance + fDistance);
                if (StaticContent.isRayAttenuationWithDistance)
                {
                    tColour += tReflectedColour * Reflectivity * Math.Min(1, StaticContent.RayAttenuation / (totalDistance + fDistance));
                }
                else
                {
                    tColour += tReflectedColour * Reflectivity;
                }

            }            

            if (tColour.R > 255) tColour.R = 255;
            if (tColour.G > 255) tColour.G = 255;
            if (tColour.B > 255) tColour.B = 255;

            return tColour;
        }

Eu me baseei fortemente no modelo de Phong para criar este método. Fiz algumas alteracões fisicamente incorretas, porém com resultados visuais melhores como a atenuacão pela distancia dos raios refletidos. Observe que quando estou chamando tracer.Ray, estou fazendo uma chamada recursiva, uma vez que foi um tracer.Ray que chamou este método Shade.

A implementacão disponível no repositório é multiThread (dá pra ver cada parte da tela sendo desenhado =P), faz correcão de Gama (não comentei sobre isso neste artigo) e têm Antialiasing.

Projeto no sourceforgehttp://sourceforge.net/projects/learnraytracing/

A nVidia, um tempo atrás anunciou um RayTracer interativo (Real-Time). Vale a pena conferir http://www.nvidia.com/object/io_1249366628071.html os resultados.
É isso pessoal, até a próxima =P

 

, , , , ,

  1. #1 by http://news.haveinc.xyz/haveincxyz/326.asp on 30 de abril de 2017 - 3:20 pm

    I got this particular present concerning our mom for Holiday considering she is actually the best jewelry freak. All something she cannot wear far to are, bracelets. I bought the lady the appeal bracelet additionally whenever she opened things yesterday she absolutley enjoyed it! This time the problem is actually, her acquiring that on to down with herself. Haha… total awesome item, things shipped furthermore came very early plus my personal mother is actually experiencing information technology. Thank your.

  2. #2 by http://news.yourcartier.xyz/yourcartierxyz/206.asp on 30 de abril de 2017 - 3:21 pm

    I had gotten that present to the mother for Christmas time as she is actually your precious jewelry freak. Some sort of one thing she does not put on a great deal concerning was, bracelets. I bought this girl it charm bracelet additionally after she started that it yesterday she absolutley liked this! Right now the issue is actually, this girl getting it concerning to off with herself. Haha… in general awesome item, things transported furthermore came incredibly early and/or my mom are enjoying that it. Thank one.

  3. #3 by http://news.ingdaily.com/ingdaily/1030.asp on 30 de abril de 2017 - 3:36 pm

    It system was in such a awesome expense I never ever considered that top quality is quite excellent. Its perfect. Your mom can love it concerning Xmas morning whenever she opens up gifts therefore looks like I devoted a great deal more, then again rates had been exclusively very good!!

  4. #4 by Tabung Pemadam kebakaran on 30 de abril de 2017 - 4:05 pm

    I really like your writing style, fantastic info, appreciate it for putting up :D. “God save me from my friends. I can protect myself from my enemies.” by Claude Louis Hector de Villars.

  5. #5 by office movers in Washington DC on 30 de abril de 2017 - 4:09 pm

    Rub in your moisturizer to increase blood flow. Mix one part apple cider vinegar with raw work better

  6. #6 by http://steelecrestwinery.com/slate/26.html on 30 de abril de 2017 - 4:31 pm

    That headset had been as spectacular because inside visualize. They came immediately. I’d suggest getting a towel through each layer as you push this off w / the vapor iron. It does not steam outside along with only a steamer. Some sort of iron was required. It can be fine, so if you you should not trust personally using the towel and vapor iron, then consume it up to a expert. Perfect sound.

  7. #7 by dealing with std on 30 de abril de 2017 - 4:40 pm

    Every after in a when we opt for blogs that we read. Listed beneath would be the newest sites that we opt for

  8. #8 by http://sharpchallenge.com/fi/template/337.html on 30 de abril de 2017 - 4:53 pm

    This particular headset ended up being like stunning just as in the visualize. They arrived promptly. I would recommend choosing a towel over every one layer as you click they away w / any steam iron. It does not steam outside among merely a steamer. Your iron had been important. It is very fragile, so if you you should not trust your self with the towel to steam iron, after that need this to a certified. Gorgeous seem.

  9. #9 by http://news.spasswelt.xyz/spassweltxyz/196.asp on 30 de abril de 2017 - 5:02 pm

    I have purchased your brand regarding bracelet a number of period. Every a person is very adorable, made well, does not tarnish then meaningful depending on which kind of any you buy furthermore whom you offer this on.

  10. #10 by http://news.moncleroutletonline.xyz/moncleroutletonlinexyz/183.asp on 30 de abril de 2017 - 5:03 pm

    I have purchased your brand regarding bracelet various times. Every one is ultra attractive, has made very well, cannot tarnish as well as significant based on which one you buy to who a person offer they on.

  11. #11 by where can i buy hunter boots for cheap on 30 de abril de 2017 - 5:34 pm

    hey buddy, I ran across this web page from yahoo and start reading several of your other content. They are stunning. Pleasee continue this great work. Cheers,
    where can i buy hunter boots for cheap http://www.hunterboots.online

  12. #12 by princess birthday party chicago on 30 de abril de 2017 - 5:37 pm

    Respect to article author, some wonderful information .

  13. #13 by dating tips on 30 de abril de 2017 - 5:56 pm

    always a significant fan of linking to bloggers that I like but really don’t get quite a bit of link love from

  14. #14 by Carl Kruse on 30 de abril de 2017 - 6:15 pm

    “As a Newbie, I am constantly exploring online for articles that can be of assistance to me. Thank you”

  15. #15 by swing set installation on 30 de abril de 2017 - 6:17 pm

    Thanks again for the blog. Really Great.

  16. #16 by buy viagra on 30 de abril de 2017 - 6:18 pm

    These topics are so confusing but this helped me get the job done.

  17. #17 by http://news.cartierlovebracelet.xyz/cartierlovebraceletxyz/180.asp on 30 de abril de 2017 - 6:47 pm

    Right mother’s evening present. But, the bracelet got as well longer in order to come, therefore arrived after the vacation. My mom expressed it she loved ones bracelet, although We have maybe not noticed the lady put on information technology though. :-)

  18. #18 by http://news.louboutinreplica.xyz/louboutinreplicaxyz/188.asp on 30 de abril de 2017 - 6:48 pm

    Ideal mother’s time present. Then again, all bracelet took as well longer at come, therefore appeared after the getaway. The mother conveyed which she liked that bracelet, though I’ve maybe not spotted her don information technology however. :-)

  19. #19 by http://news.christianlouboutinshoes.xyz/christianlouboutinshoesxyz/198.asp on 30 de abril de 2017 - 6:48 pm

    Ideal mother’s evening gift. Nevertheless, your bracelet took also prolonged to arrive, therefore came after the vacation. My personal mother shown which is she liked some sort of bracelet, although I’ve not noticed the lady don that however. :-)

  20. #20 by Alat pemadam api on 30 de abril de 2017 - 7:11 pm

    Very interesting topic, appreciate it for putting up.

  21. #21 by cheap ecco shoes on 30 de abril de 2017 - 7:48 pm

    Ive realised a whole lot from reading through this post and I hope that I can catch up on other these kinds of posts soon.
    cheap ecco shoes http://www.eccoonlineshop.com

  22. #22 by viagra for sale on 30 de abril de 2017 - 7:52 pm

    At last! Someone who understands! Thanks for posting!

  23. #24 by BDSM on 30 de abril de 2017 - 8:20 pm

    Here are some of the web pages we advise for our visitors

  24. #25 by http://news.cartierreplica.win/cartierreplicawin/402.asp on 30 de abril de 2017 - 8:51 pm

    This goods is in that ideal rate I not considered the particular quality is hence excellent. It is breathtaking. Your mama will likely enjoy information technology regarding Holiday day after she opens up present and it looks like I spent a lot more, and yet rates was just great!!

  25. #26 by http://news.vancleefreplica.xyz/vancleefreplicaxyz/208.asp on 30 de abril de 2017 - 8:52 pm

    It product or service was at such a very good price tag I did not consideration the particular excellence is and so excellent. Its gorgeous. The mama may adore that on top of Holiday early morning once she opens gifts therefore appears like I spent alot more, still cost had been only awesome!!

  26. #27 by ways to tell your partner you have an std on 30 de abril de 2017 - 8:56 pm

    Here are several of the sites we recommend for our visitors

  27. #28 by http://www.forpackningsutveckling.se/code/44.html on 30 de abril de 2017 - 9:46 pm

    This particular headset ended up being since beautiful because in picture. That appeared quickly. I would advise using a towel done each and every coating as you press that out w / letter vapor iron. It doesn’t steam over alongside merely a steamer. Some sort of iron is essential. It can be sensitive, so if you cannot trust your self with all the towel then vapor iron, after that take this to a professional. Stunning sound.

  28. #29 by Alat pemadam kebakaran on 30 de abril de 2017 - 9:53 pm

    It is perfect time to make some plans for the future and it is time to be happy. I have read this post and if I could I desire to suggest you few interesting things or tips. Perhaps you could write next articles referring to this article. I want to read more things about it!

  29. #30 by Whip on 30 de abril de 2017 - 10:10 pm

    we prefer to honor numerous other world-wide-web websites on the web, even when they arent linked to us, by linking to them. Underneath are some webpages worth checking out

  30. #31 by http://stefanbakosab.se/web/html/331.html on 30 de abril de 2017 - 10:11 pm

    This one headset was like breathtaking because within the image. Things came promptly. I’d advise choosing a towel over every single level while you click things out w / letter steam iron. It does not steam off alongside merely a steamer. The actual iron ended up being required. It is very sensitive, when you never trust your self using the towel and/or vapor iron, well take information technology up to a professional. Stunning noise.

  31. #32 by http://www.borgakungen.nu/data/12.html on 30 de abril de 2017 - 10:12 pm

    This one headset had been because spectacular since inside visualize. Information technology appeared quickly. I would advise choosing a towel over every one coating as you hit this out w / your vapor iron. It doesn’t vapor over alongside merely a steamer. On iron was necessary. It can be delicate, when you you should not trust personally with the towel then vapor iron, next bring it to a certified. Pretty noise.

  32. #33 by http://rbvonlineviagraproviders.top/ on 30 de abril de 2017 - 10:42 pm

    That kind of thinking shows you’re an expert

  33. #34 by Catheryn Croft on 30 de abril de 2017 - 10:54 pm

    best online shoe shopping

  34. #35 by http://news.vancleefreplica.pw/vancleefreplicapw/417.asp on 30 de abril de 2017 - 11:12 pm

    Well mother’s evening gifts. Nevertheless, that bracelet took quite extended to arrive, and it came after the holiday. The mother conveyed that she liked some sort of bracelet, although We have never noticed the lady wear things but. :-)

  35. #36 by http://news.ordersoho.com/ordersoho/1006.asp on 30 de abril de 2017 - 11:13 pm

    Right mother’s evening present. However, that bracelet took too longer to appear, therefore arrived after the vacation. My mother expressed which is she loved all bracelet, although We have not really viewed this girl don this though. :-)

  36. #37 by http://news.portside.xyz/portsidexyz/334.asp on 30 de abril de 2017 - 11:13 pm

    Good mother’s time gifts. Conversely, your bracelet took besides very long at arrive, therefore came after the vacation. The mother expressed which she liked their bracelet, even though I’ve maybe not noticed this girl don it still. :-)

1 538 539 540
(não será publicado)