2015-09-28 3 views
1

Я делаю видео-игру в единстве и решил использовать трассировку лучей. У меня есть код, но как вы увидите через секунду. Это не точно рендеринг по кадре. Вот мой raytracing code, это основной скрипт, прикрепленный к основной камере.Как сделать в реальном времени Raytracing в единстве с C#

using UnityEngine; 
using System.Collections; 

public class RayTracer : MonoBehaviour 
{ 

    public Color backgroundColor = Color.black; 
    public float RenderResolution = 1f; 
    public float maxDist = 100f; 
    public int maxRecursion = 4; 


    private Light[] lights; 
    private Texture2D renderTexture; 

    void Awake() 
    { 
     renderTexture = new Texture2D((int)(Screen.width * RenderResolution), (int)(Screen.height * RenderResolution)); 
     lights = FindObjectsOfType(typeof(Light)) as Light[]; 
    } 

    void Start() 
    { 
     RayTrace(); 
    } 

    void OnGUI() 
    { 
     GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), renderTexture); 
    } 

    void RayTrace() 
    { 
     for (int x = 0; x < renderTexture.width; x++) 
     { 
      for (int y = 0; y < renderTexture.height; y++) 
      { 
       Color color = Color.black; 
       Ray ray = GetComponent<Camera>().ScreenPointToRay(new Vector3(x/RenderResolution, y/RenderResolution, 0)); 

       renderTexture.SetPixel(x, y, TraceRay(ray, color, 0)); 
      } 
     } 

     renderTexture.Apply(); 
    } 

    Color TraceRay(Ray ray, Color color, int recursiveLevel) 
    { 

     if (recursiveLevel < maxRecursion) 
     { 
      RaycastHit hit; 
      if (Physics.Raycast(ray, out hit, maxDist)) 
      { 
       Vector3 viewVector = ray.direction; 
       Vector3 pos = hit.point + hit.normal * 0.0001f; 
       Vector3 normal = hit.normal; 

       RayTracerObject rto = hit.collider.gameObject.GetComponent<RayTracerObject>(); 
       //Does the object we hit have that script? 
       if (rto == null) 
       { 
        var GO = hit.collider.gameObject; 
        Debug.Log("Raycast hit failure! On " + GO.name + " position " + GO.transform.position.ToString()); 
        return color; //exit out 
       } 

       Material mat = hit.collider.GetComponent<Renderer>().material; 
       if (mat.mainTexture) 
       { 
        color += (mat.mainTexture as Texture2D).GetPixelBilinear(hit.textureCoord.x, hit.textureCoord.y); 
       } 
       else 
       { 
        color += mat.color; 
       } 

       color *= TraceLight(rto, viewVector, pos, normal); 

       if (rto.reflectiveCoeff > 0) 
       { 
        float reflet = 2.0f * Vector3.Dot(viewVector, normal); 
        Ray newRay = new Ray(pos, viewVector - reflet * normal); 
        color += rto.reflectiveCoeff * TraceRay(newRay, color, recursiveLevel + 1); 
       } 

       if (rto.transparentCoeff > 0) 
       { 
        Ray newRay = new Ray(hit.point - hit.normal * 0.0001f, viewVector); 
        color += rto.transparentCoeff * TraceRay(newRay, color, recursiveLevel + 1); 
       } 
      } 
     } 

     return color; 

    } 

    Color TraceLight(RayTracerObject rto, Vector3 viewVector, Vector3 pos, Vector3 normal) 
    { 
     Color c = RenderSettings.ambientLight; 

     foreach (Light light in lights) 
     { 
      if (light.enabled) 
      { 
       c += LightTrace(rto, light, viewVector, pos, normal); 
      } 
     } 
     return c; 
    } 

    Color LightTrace(RayTracerObject rto, Light light, Vector3 viewVector, Vector3 pos, Vector3 normal) 
    { 


     float dot, distance, contribution; 
     Vector3 direction; 
     switch (light.type) 
     { 
      case LightType.Directional: 
       contribution = 0; 
       direction = -light.transform.forward; 
       dot = Vector3.Dot(direction, normal); 
       if (dot > 0) 
       { 
        if (Physics.Raycast(pos, direction, maxDist)) 
        { 
         return Color.black; 
        } 

        if (rto.lambertCoeff > 0) 
        { 
         contribution += dot * rto.lambertCoeff; 
        } 
        if (rto.reflectiveCoeff > 0) 
        { 
         if (rto.phongCoeff > 0) 
         { 
          float reflet = 2.0f * Vector3.Dot(viewVector, normal); 
          Vector3 phongDir = viewVector - reflet * normal; 
          float phongTerm = max(Vector3.Dot(phongDir, viewVector), 0.0f); 
          phongTerm = rto.reflectiveCoeff * Mathf.Pow(phongTerm, rto.phongPower) * rto.phongCoeff; 

          contribution += phongTerm; 
         } 
         if (rto.blinnPhongCoeff > 0) 
         { 
          Vector3 blinnDir = -light.transform.forward - viewVector; 
          float temp = Mathf.Sqrt(Vector3.Dot(blinnDir, blinnDir)); 
          if (temp != 0.0f) 
          { 
           blinnDir = (1.0f/temp) * blinnDir; 
           float blinnTerm = max(Vector3.Dot(blinnDir, normal), 0.0f); 
           blinnTerm = rto.reflectiveCoeff * Mathf.Pow(blinnTerm, rto.blinnPhongPower) * rto.blinnPhongCoeff; 

           contribution += blinnTerm; 
          } 
         } 
        } 
       } 
       return light.color * light.intensity * contribution; 
      case LightType.Point: 
       contribution = 0; 
       direction = (light.transform.position - pos).normalized; 
       dot = Vector3.Dot(normal, direction); 
       distance = Vector3.Distance(pos, light.transform.position); 
       if ((distance < light.range) && (dot > 0)) 
       { 
        if (Physics.Raycast(pos, direction, distance)) 
        { 
         return Color.black; 
        } 

        if (rto.lambertCoeff > 0) 
        { 
         contribution += dot * rto.lambertCoeff; 
        } 
        if (rto.reflectiveCoeff > 0) 
        { 
         if (rto.phongCoeff > 0) 
         { 
          float reflet = 2.0f * Vector3.Dot(viewVector, normal); 
          Vector3 phongDir = viewVector - reflet * normal; 
          float phongTerm = max(Vector3.Dot(phongDir, viewVector), 0.0f); 
          phongTerm = rto.reflectiveCoeff * Mathf.Pow(phongTerm, rto.phongPower) * rto.phongCoeff; 

          contribution += phongTerm; 
         } 
         if (rto.blinnPhongCoeff > 0) 
         { 
          Vector3 blinnDir = -light.transform.forward - viewVector; 
          float temp = Mathf.Sqrt(Vector3.Dot(blinnDir, blinnDir)); 
          if (temp != 0.0f) 
          { 
           blinnDir = (1.0f/temp) * blinnDir; 
           float blinnTerm = max(Vector3.Dot(blinnDir, normal), 0.0f); 
           blinnTerm = rto.reflectiveCoeff * Mathf.Pow(blinnTerm, rto.blinnPhongPower) * rto.blinnPhongCoeff; 

           contribution += blinnTerm; 
          } 
         } 
        } 
       } 
       if (contribution == 0) 
       { 
        return Color.black; 
       } 
       return light.color * light.intensity * contribution; 
      case LightType.Spot: 
       contribution = 0; 
       direction = (light.transform.position - pos).normalized; 
       dot = Vector3.Dot(normal, direction); 
       distance = Vector3.Distance(pos, light.transform.position); 
       if (distance < light.range && dot > 0) 
       { 
        float dot2 = Vector3.Dot(-light.transform.forward, direction); 
        if (dot2 > (1 - light.spotAngle/180)) 
        { 
         if (Physics.Raycast(pos, direction, distance)) 
         { 
          return Color.black; 
         } 
         if (rto.lambertCoeff > 0) 
         { 
          contribution += dot * rto.lambertCoeff; 
         } 
         if (rto.reflectiveCoeff > 0) 
         { 
          if (rto.phongCoeff > 0) 
          { 
           float reflet = 2.0f * Vector3.Dot(viewVector, normal); 
           Vector3 phongDir = viewVector - reflet * normal; 
           float phongTerm = max(Vector3.Dot(phongDir, viewVector), 0.0f); 
           phongTerm = rto.reflectiveCoeff * Mathf.Pow(phongTerm, rto.phongPower) * rto.phongCoeff; 

           contribution += phongTerm; 
          } 
          if (rto.blinnPhongCoeff > 0) 
          { 
           Vector3 blinnDir = -light.transform.forward - viewVector; 
           float temp = Mathf.Sqrt(Vector3.Dot(blinnDir, blinnDir)); 
           if (temp != 0.0f) 
           { 
            blinnDir = (1.0f/temp) * blinnDir; 
            float blinnTerm = max(Vector3.Dot(blinnDir, normal), 0.0f); 
            blinnTerm = rto.reflectiveCoeff * Mathf.Pow(blinnTerm, rto.blinnPhongPower) * rto.blinnPhongCoeff; 

            contribution += blinnTerm; 
           } 
          } 
         } 
        } 
       } 
       if (contribution == 0) 
       { 
        return Color.black; 
       } 
       return light.color * light.intensity * contribution; 
     } 
     return Color.black; 
    } 

    float max(float x0, float x1) 
    { 
     return x0 > x1 ? x0 : x1; 
    } 
} 

И это код прилагается к объектам в сцене

using UnityEngine; 
using System.Collections; 

public class RayTracerObject : MonoBehaviour 
{ 

    public float lambertCoeff = 1f; 

    public float reflectiveCoeff = 0f; 

    public float phongCoeff = 1f; 
    public float phongPower = 2f; 

    public float blinnPhongCoeff = 1f; 
    public float blinnPhongPower = 2f; 

    public float transparentCoeff = 0f; 


    public Color baseColor = Color.gray; 

    void Awake() 
    { 
     if (!GetComponent<Renderer>().material.mainTexture) 
     { 
      GetComponent<Renderer>().material.color = baseColor; 
     } 
    } 
} 

Как бы я идти об этом? И какой будет код?

ответ

5

Хотя raytracing в основной нити является вполне приемлемым дизайном, вероятно, это не то, что вы хотите в Unity, поскольку оно блокирует все остальное.

Теперь вы можете, возможно, создать дочерний поток для выполнения трассировки лучей и первичную нить визуализировать результаты. Проблема заключается в том, что ни один из подходов не использует графический процессор, который в первую очередь поражает точку, используя Unity.

Как сделать в режиме реального времени Raytracing в единстве с C#

Это все зависит от того, что ваша сцена состоит и как вы собираетесь сделать его. Вы, возможно, могли бы сделать что-то простое в режиме реального времени при низком разрешении, однако рендеринг с разумным разрешением экрана и с разумными уровнями луча, например, количество рекурсивных световых лучей, отлитых с отражающими или пропускающими материалами, было бы намного сложнее.

Вместо этого я хотел бы призвать вас, чтобы следовать за изменением тенденции в трассировке лучей, где в режиме реального времени Raytracing теперь выполняется на GPU с использованием методов, известных как общего назначения GPU или GPGPU. nVidia имеет некоторые переговоры по этому вопросу и доступны на YouTube. Here is my sample Unity GPGPU galaxy simulation, что может оказаться полезным в качестве фона для GPGPU.

Sample ядро ​​GPGPU просто, чтобы показать вам, что GPGPU о:

// File: Galaxy1Compute.compute 

// Each #kernel tells which function to compile; you can have many kernels 
#pragma kernel UpdateStars 

#include "Galaxy.cginc" 

// blackmagic 
#define BLOCKSIZE 128 

RWStructuredBuffer<Star> stars; 

Texture2D HueTexture; 

// refer to http://forum.unity3d.com/threads/163591-Compute-Shader-SamplerState-confusion 
SamplerState samplerHueTexture; 

// time ellapsed since last frame 
float deltaTime; 

const float Softening=3e4f; 
#define Softening2 Softening * Softening 

static float G = 6.67300e-11f; 
static float DefaultMass = 1000000.0f; 

// Do a pre-calculation assuming all the stars have the same mass 
static float GMM = G*DefaultMass*DefaultMass; 


[numthreads(BLOCKSIZE,1,1)] 
void UpdateStars (uint3 id : SV_DispatchThreadID) 
{ 
    uint i = id.x; 
    uint numStars, stride; 
    stars.GetDimensions(numStars, stride); 

    float3 position = stars[i].position; 
    float3 velocity = stars[i].velocity; 

    float3 A=float3(0,0,0); 

    [loop] 
    for (uint j = 0; j < numStars; j++) 
    {  
     if (i != j) 
     { 
      float3 D = stars[j].position - stars[i].position; 
      float r = length(D); 
      float f = GMM/(r * r + Softening2); 
      A += f * normalize(D); 
     } 
    } 

    velocity += A * deltaTime; 
    position += velocity * deltaTime; 

    if (i < numStars) 
    { 
     stars[i].velocity = velocity; 
     stars[i].position = position; 
     stars[i].accelMagnitude = length(A); 
    } 


} 

Кроме того, существуют некоторые прекрасные книги на эту тему. Real-time Volume Graphics, хотя он охватывает объемы, он охватывает кастинг лучей - суть трассировки лучей. Самый сложный сдвиг в парадигме - это запись для GPGPU, как только вы это понимаете, написание GPGPU raytracer - это легкий шаг от шейдеров GPGPU.

enter image description here

Чудесный том, чтобы сопровождать любой Raytrace автор является Physically Based Rendering книга Мэтта Фарра в (есть второе издание, но я не читал, что)

enter image description here

Смежные вопросы