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 Lee el articulo completo on 24 de março de 2017 - 5:09 am

    Wow! Thank you! I constantly wanted to write on my website something like that. Can I include a fragment of your post to my blog?

  2. #2 by Recursos de ayuda on 24 de março de 2017 - 5:28 am

    Im obliged for the article.Thanks Again. Will read on

  3. #3 by http://www.chalet-la-giettaz.com/louboutinshoes.aspx on 24 de março de 2017 - 5:38 am

    So once I purchased that I was thinking things was going to be the best cool although inexpensively prepared bracelet best concerning stocking stuffers. Then again it’s prepared amazingly furthermore is actually heavy and so its not synthetic otherwise effortlessly chipped steel. My mom adored this. This can be a little bit on the slight part so if you have a bigger wrist i wouldnt recommend it but it is quite well worth the deal price!!

  4. #4 by http://www.bornagainint.org/toys-r-us-promotional-codes-canada.html on 24 de março de 2017 - 5:40 am

    Thanks alot – your answer solved all my problems after several days struggling

  5. #6 by national aquarium discounts on 24 de março de 2017 - 6:32 am

    I will be putting this dazzling insight to good use in no time.

  6. #7 by for more information on 24 de março de 2017 - 6:39 am

    Magnificent site. Lots of useful info here.

  7. #8 by http://finedocs.com/loubouton.asp on 24 de março de 2017 - 6:48 am

    I had gotten this one headphonesfor the mother towards moms time, and she completely liked this! Their actually sweet headphonesand suggesting regarding the card just that goes in box is really sentimental!! And the excellent of beads are ideal!

  8. #9 by Restaraunt reviews on 24 de março de 2017 - 6:58 am

    Thank you for the auspicious writeup. It in fact was a amusement account it. Look advanced to far added agreeable from you! By the way, how could we communicate?

  9. #10 by Best cam sites on 24 de março de 2017 - 7:07 am

    After all, what a great site and informative posts, I will upload inbound link – bookmark this web site? Regards, Reader.

  10. #11 by miami real estate broker on 24 de março de 2017 - 7:17 am

    The quality of our personalized selection of fine Italian made crystal serving selection remain unchallenged.

  11. #12 by ERP Systems in India on 24 de março de 2017 - 7:37 am

    Im grateful for the blog article. Awesome.

  12. #13 by Cartier Tank Replica on 24 de março de 2017 - 8:19 am

    My son gave me personally your of moms day. He understands im maybe not concerning price tag although just what comes from that center. I cannot trust it has my preferred shade furthermore truly fits my wrist. I do not might find bracelets to suit headphonesour tiny wrist. Extremely happy to have actually recieved this as a gift.

  13. #14 by Reggie Clance on 24 de março de 2017 - 9:16 am

    I take delight in reading what you had to express, You have an remarkable knowledge on the subject informationa nd I look forward to examing more of what you have to say. I will pay attention and bookmark your post and come back to your internet site when an update is posted.

  14. #15 by http://www.circi.se/wp-includes/fonts/page/328.html on 24 de março de 2017 - 9:20 am

    it comes down in a breathtaking box styles awesome still it really is a bit little additionally of our moms wrist however it looks very good only desire that it was further

  15. #16 by Best cam sites on 24 de março de 2017 - 9:36 am

    I love your blog.. very nice colors & theme. Did you create this website yourself? Plz reply back as I’m looking to create my own blog and would like to know wheere u got this from. thanks

  16. #17 by http://www.ljusihus.se/moncleroutlet.aspx on 24 de março de 2017 - 11:21 am

    And so when i requested it I was thinking information technology is going to be letter awesome and yet cheaply created bracelet most appropriate towards stocking stuffers. Then again it is created wonderfully and also is hefty so the not really plastic or perhaps quickly chipped metal. My mom enjoyed it. This is a little regarding the mini part when you enjoy a larger wrist i wouldnt advocate information technology however it is quite well worth the actual deal rates!!

  17. #18 by http://www.alvtank.se/redbottom.asp on 24 de março de 2017 - 1:20 pm

    I had gotten it headphonesfor our mother for moms time, plus she absolutely adored that! It really is actually pretty headphonesand the suggesting in the card which is will come in the container is really emotional!! And grade of beads is ideal!

  18. #19 by http://www.louboutinreplica.pw/ on 24 de março de 2017 - 1:21 pm

    I got the headphonesconcerning my personal mother of parents time, additionally she absolutely enjoyed it! It really is truly pretty headphonesand the stating in the card just that will come in package is really emotional!! As well as the quality of beads is great!

  19. #20 by Kitchens Wimbledon on 24 de março de 2017 - 1:29 pm

    “I think you have a great page here� today was my first time coming here.. I just happened to find it doing a google search. anyway, good post.. I�ll be bookmarking this page for sure.”

  20. #21 by Kitchen Showrooms London on 24 de março de 2017 - 1:46 pm

    “Hi, very great web site you have made. I enjoyed reading this posting. I did want to publish a comment to tell you that the design of this site is very aesthetically sweet.”

  21. #22 by navigieren sie zu dieser seite on 24 de março de 2017 - 2:47 pm

    Die Idee: Durch das Strecken entstehen Microverletzungen den Schwellkörpern, die wieder verheilen, das so langsam entstehende zusätzliche Gewebe vergrößert den Penis.

  22. #23 by hobbyhuren halle on 24 de março de 2017 - 3:38 pm

    Ich habe auch geschrieben das mein Kreditlimit zur zeit überzogen ist da ich auch kosten übernommen habe zum umbau meines Elternhauses und ich dahe das Geld nich schicken kann.

  23. #24 by desentupidora on 24 de março de 2017 - 3:49 pm

    Wanted to drop a comment and let you know your Feed isnt working today. I tried including it to my Google reader account and got absolutely nothing.

  24. #25 by advance auto insurance salt lake city on 24 de março de 2017 - 3:54 pm

    I could see this as a very handy trim carpenters tool for cutting out receptacles in Tall base board…It it can hold up to that type of use…But i’ll probably stick to my Jigsaw.

  25. #26 by http://news.hermesreplica.win/hermesreplicawin/160.asp on 24 de março de 2017 - 4:00 pm

    I had gotten this one gift of the mom of Xmas because she is a jewelry freak. Your one thing she cannot use a lot concerning was, bracelets. I purchased the lady that appeal bracelet plus anytime she opened things yesterday she absolutley liked they! Nowadays the issue was, the lady obtaining it concerning plus off of through by herself. Haha… general great product or service, that it shipped furthermore appeared really early then my personal mother was experiencing that. Thank people.

  26. #27 by http://news.haveinc.xyz/haveincxyz/38.asp on 24 de março de 2017 - 4:01 pm

    I had gotten this gifts concerning the mother of Xmas as she is actually letter jewelry freak. Ones one thing she cannot wear much to is actually, bracelets. I purchased her it allure bracelet furthermore where she started they yesterday she absolutley enjoyed that! This time the problem is, her obtaining that it on additionally off of by by herself. Haha… overall ideal supplement, that it shipped plus arrived quite very early plus our mom was enjoying they. Thank your.

  27. #28 by http://news.vancleefreplica.top/vancleefreplicatop/58.asp on 24 de março de 2017 - 4:01 pm

    it comes within a gorgeous package looks very good but its a bit small actually to our mothers wrist but it looks great only want that is further

  28. #29 by http://news.haveinc.xyz/haveincxyz/57.asp on 24 de março de 2017 - 4:02 pm

    it comes down in a breathtaking package styles great then again their slightly slight truly of my personal moms wrist however it appearances awesome simply really want they is longer

  29. #30 by Geico on 24 de março de 2017 - 4:03 pm

    I wish to thank you regarding natural feedback which really determined in obtaining novel conclusions. The most preferable feature to obtain prices that is online process which provides the cheapest deals on landlords insurance from good known companies as american family located in United States.

  30. #31 by auto insurance ice fishing on 24 de março de 2017 - 5:18 pm

    It’s good to get a fresh way of looking at it.

  31. #32 by online car ins on 24 de março de 2017 - 5:21 pm

    LizHow extensive is your archive regarding the regions Fire Brigades Pre – 1974?- I’m thinking of doing a ‘prequel’ to my 1st book (above).I knew Harry Louvre as the Brigade Union Rep (post ’74) -his story featured in the ‘Flames Across The Tyne’ book, now out of print.RegardsDW

  32. #33 by http://www.klarsikt.se/wp-content/plugins/ait-monop/272.html on 24 de março de 2017 - 6:21 pm

    I had gotten that present for the mom concerning Holiday as she is actually any jewelry freak. The actual one thing she does not put on much concerning was, bracelets. I purchased her your allure bracelet then after she opened they yesterday she absolutley enjoyed that it! This time the problem was, her getting they on top of and/or down by just herself. Haha… total great system, information technology transported then arrived incredibly very early then the mom is actually enjoying that it. Thank shoppers.

  33. #34 by coyote buttes lottery on 24 de março de 2017 - 6:36 pm

    party Poker.com view of Three Gorges | Wonder Travel Blog

  34. #35 by http://www.yosemitesoap.com/best-tenant-insurance.html on 24 de março de 2017 - 7:54 pm

    Thinking like that is really impressive

  35. #36 by st george chiropractor on 24 de março de 2017 - 8:02 pm

    Thanks so much for the post.Thanks Again. Fantastic.

1 474 475 476
(não será publicado)