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 geico auto insurance quotes on 22 de julho de 2017 - 2:53 pm

    Very interesting information!Perfect just what I was looking for!

  2. #2 by Geico motorcycle quotes on 22 de julho de 2017 - 3:35 pm

    This web site is my aspiration, really excellent style and design and perfect content material.

  3. #3 by Kristal on 22 de julho de 2017 - 4:04 pm

    Have actually been taking little over a month.

  4. #4 by UK Christian Louboutin Replica on 22 de julho de 2017 - 4:37 pm

    This item had been in such a very good expense I never ever attention some sort of high quality is therefore exceptional. Its breathtaking. This one mother will certainly really like they on Xmas early morning after she starts gift therefore appears as though I devoted a lot more, and yet cost was really very good!!

  5. #5 by 14056 on 22 de julho de 2017 - 9:16 pm

    Hey I know this is off topic but I was wondering if you knew
    of any widgets I could add to my blog that automatically tweet my
    newest twitter updates. I’ve been looking for a plug-in like this for
    quite some time and was hoping maybe you would have some experience with something like
    this. Please let me know if you run into anything. I truly enjoy reading your blog and I look
    forward to your new updates.

  6. #6 by keen outlet online on 22 de julho de 2017 - 9:56 pm

    Thought I would comment and say cool theme, did you code it on your own? It looks really good!
    keen outlet online http://www.keenoutlet.online

  7. #7 by live sex on 22 de julho de 2017 - 10:34 pm

    The first time I came face to face with pornography I was eight years old. Yes, you read it right – I was an eight year old child. I did not experience it alone; an older cousin sat next to me and made jokes about the naked muscled men on the TV. At eight years old, my first exposure to sex was established through a living room television screen, followed by a couple of jokes. This experience never left me.

  8. #8 by cheap parajumpers jackets on 22 de julho de 2017 - 10:41 pm

    taw suede fargue hammerste prentace hark debility indio Olive
    cheap parajumpers jackets http://www.cheapparajumpers.online

  9. #9 by low income car insurance Erie PA on 22 de julho de 2017 - 10:42 pm

    Kudos to you! I hadn’t thought of that!

  10. #10 by affordable car insurance Waxhaw NC on 22 de julho de 2017 - 10:49 pm

    That’s really thinking out of the box. Thanks!

  11. #11 by click now on 22 de julho de 2017 - 10:56 pm

    usually posts some incredibly exciting stuff like this. If you are new to this site

  12. #12 by more on 23 de julho de 2017 - 2:22 am

    just beneath, are quite a few entirely not related web pages to ours, even so, they are certainly really worth going over

  13. #13 by car insurance with no license in Levittown NY on 23 de julho de 2017 - 2:26 am

    That’s a smart way of looking at the world.

  14. #14 by Vivienne Westwood online on 23 de julho de 2017 - 4:14 am

    Wonderful story. I was checking continuously to this weblog and I am really impressed! Extremely educational information, especially the 4th sentences. I really want this kind of info. I used to be seeking this particular knowledge for weeks. Thanks and good luck.
    Vivienne Westwood online http://www.viviennewestwood.online

  15. #15 by http://www.bazaaralosra.com/wp-black.php on 23 de julho de 2017 - 4:24 am

    This particular supplement had been at such a very good price tag I never ever thought the actual grade would be hence excellent. Its perfect. Your mom is going to adore that it regarding Christmas day where she starts gift and it looks like I spent significantly more, still cost was just ideal!!

  16. #16 by hermes belt replica on 23 de julho de 2017 - 4:25 am

    The product or service ended up being at such a awesome rate I do not believed your excellence is so that excellent. It’s stunning. It mama might enjoy this on top of Christmas time early morning after she opens present therefore styles as though I invested more, however pricing had been really very good!!

  17. #17 by PORN on 23 de julho de 2017 - 4:43 am

    Watch live sex on our site

  18. #18 by site protection on 23 de julho de 2017 - 4:52 am

    we came across a cool web site that you simply could possibly take pleasure in. Take a look for those who want

  19. #19 by teologia online on 23 de julho de 2017 - 6:27 am

    Heya i’m for the first time here. I came across this board and I find It truly useful & it helped me out a lot.
    I am hoping to give something back and help others such as you helped me.

  20. #20 by cheap nike roshe run on 23 de julho de 2017 - 7:06 am

    We have bought your brand name of bracelet a number of circumstances. Every a person is extremely attractive, done well, does not tarnish then important depending on which kind of an you purchase and that people give information technology in order to.

  21. #21 by building link 90 washington on 23 de julho de 2017 - 7:42 am

    one of our guests not too long ago encouraged the following website

  22. #22 by http://www.cheapmoncler.xyz/ on 23 de julho de 2017 - 9:42 am

    It goods had been with such a great amount I not attention that top quality would be and so excellent. Its gorgeous. The mom is going to like this on Christmas morning where she opens gift and it appearance as though I spent a lot more, however prices was just very good!!

  23. #23 by lv store location on 23 de julho de 2017 - 10:31 am

    Great post, where can I find out more about this?
    lv store location http://www.ebayeu.co.uk

  24. #24 by read the article on 23 de julho de 2017 - 11:01 am

    one of our guests not long ago suggested the following website

  25. #25 by backlinks quality checker on 23 de julho de 2017 - 12:48 pm

    that is the finish of this article. Right here you will find some sites that we assume you will appreciate, just click the hyperlinks over

  26. #26 by arcteryx jackets store on 23 de julho de 2017 - 1:40 pm

    This is my second time reading from this blog and it just gets better and better.
    arcteryx jackets store http://www.arcteryxstore.online

  27. #27 by list of auto insurances in Dearborn Heights MI on 23 de julho de 2017 - 1:45 pm

    It’s great to find someone so on the ball

  28. #28 by find out here on 23 de julho de 2017 - 2:21 pm

    we came across a cool web-site that you just may possibly love. Take a appear in the event you want

  29. #29 by hermes bracelet replica on 23 de julho de 2017 - 2:46 pm

    I’ve bought your brand name out of bracelet some period. Every a person is very attractive, made very well, cannot tarnish to important depending on which kind of single you buy then which your give it towards.

  30. #30 by no down payment car insurance in Cape Coral FL on 23 de julho de 2017 - 2:51 pm

    No question this is the place to get this info, thanks y’all.

  31. #31 by singapore pr application on 23 de julho de 2017 - 3:01 pm

    Good posts!

  32. #32 by Mississippi vacation rentals on 23 de julho de 2017 - 4:45 pm

    please check out the sites we adhere to, like this one particular, because it represents our picks in the web

  33. #33 by balmain for sale on 23 de julho de 2017 - 4:49 pm

    Thanks for taking the time to talk about this, I feel fervently about this and I take pleasure in learning about this topic. Please, as you gain information, please update this blog with more information. I have found it very useful. There have to be charging stations everywhere.
    balmain for sale http://www.leove.co.uk

  34. #34 by pop over to this site on 23 de julho de 2017 - 4:58 pm

    please stop by the internet sites we follow, such as this 1, because it represents our picks from the web

  35. #35 by hack tool for any game on 23 de julho de 2017 - 5:07 pm

    Excellent post. Keep posting such kind of info on your page.
    Im really impressed by your site.
    Hello there, You’ve done a great job. I’ll certainly digg it and for my part
    recommend to my friends. I am confident they will be
    benefited from this site.

  36. #36 by Pascagoula hotel on 23 de julho de 2017 - 5:21 pm

    always a large fan of linking to bloggers that I love but do not get lots of link adore from

  37. #37 by find out on 23 de julho de 2017 - 5:57 pm

    we like to honor lots of other online websites around the net, even if they arent linked to us, by linking to them. Below are some webpages worth checking out

  38. #38 by www.my-exact.de on 23 de julho de 2017 - 5:58 pm

    What’s up to every single one, it’s actually a good for me
    to pay a quick visit this website, it includes important Information.

  39. #39 by pr application on 23 de julho de 2017 - 6:14 pm

    Good posts!

  40. #40 by Biloxi vacation rentals on 23 de julho de 2017 - 6:25 pm

    here are some hyperlinks to sites that we link to for the reason that we feel they may be worth visiting

  41. #41 by Superior Quality,wide varieties on 23 de julho de 2017 - 6:46 pm

    Very interesting information!Perfect just what I was searching for!

  42. #42 by mammut outlet store on 23 de julho de 2017 - 6:54 pm

    This is a massive and a very interesting post to check on this awesome site! Almost never post any feedback but now i just did not resist
    mammut outlet store http://www.mammutstore.online

  43. #43 by cartier love ring replica on 23 de julho de 2017 - 7:25 pm

    This one device ended up being in that great rates I never considered ones good is therefore exceptional. It is striking. This one mama is going to like it on Christmas time early morning anytime she starts gifts and it looks like I spent significantly more, and yet rates ended up being exclusively ideal!!

  44. #44 by Southwind Resort on 23 de julho de 2017 - 7:31 pm

    The details mentioned inside the report are a number of the top available

1 669 670 671
(não será publicado)