web-dev-qa-db-ja.com

レイトレーシングで屈折を適切に処理する方法

私は現在、楽しみのためにレイトレーサーに取り組んでいますが、屈折処理に問題があります。

レイトレーサー全体のコードソースは Github上 にあります。

レンダリングの画像は次のとおりです。

Refraction bug

右の球は、1.5(ガラス)の屈折率を持つように設定されています。

屈折に加えて、次のように定義される「透明度」係数を処理したいと思います。

  • 0->オブジェクトは100%不透明です
  • 1->オブジェクトは100%透明です(元のオブジェクトの色の痕跡はありません)

この球の透明度は1です。

屈折部分を処理するコードは次のとおりです。それは見つけることができます ここのgithubで

Color handleTransparency(const Scene& scene,
                         const Ray& ray,
                         const IntersectionData& data,
                         uint8 depth)
{
  Ray refracted(RayType::Transparency, data.point, ray.getDirection());
  Float_t eta = data.material->getRefraction();

  if (eta != 1 && eta > Globals::Epsilon)
    refracted.setDirection(Tools::Refract(ray.getDirection(), data.normal, eta));
  refracted.setOrigin(data.point + Globals::Epsilon * refracted.getDirection());
  return inter(scene, refracted, depth + 1);
}

// http://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf
Float_t getFresnelReflectance(const IntersectionData& data, const Ray& ray)
{
  Float_t n = data.material->getRefraction();
  Float_t cosI = -Tools::DotProduct(ray.getDirection(), data.normal);
  Float_t sin2T = n * n * (Float_t(1.0) - cosI * cosI);

  if (sin2T > 1.0)
    return 1.0;

  using std::sqrt;
  Float_t cosT = sqrt(1.0 - sin2T);
  Float_t rPer = (n * cosI - cosT) / (n * cosI + cosT);
  Float_t rPar = (cosI - n * cosT) / (cosI + n * cosT);
  return (rPer * rPer + rPar * rPar) / Float_t(2.0);
}

Color handleReflectionAndRefraction(const Scene& scene,
                                    const Ray& ray,
                                    const IntersectionData& data,
                                    uint8 depth)
{
  bool hasReflexion = data.material->getReflexion() > Globals::Epsilon;
  bool hasTransparency = data.material->getTransparency() > Globals::Epsilon;

  if (!(hasReflexion || hasTransparency) || depth >= MAX_DEPTH)
    return 0;

  Float_t reflectance = data.material->getReflexion();
  Float_t transmittance = data.material->getTransparency();

  Color reflexion;
  Color transparency;

  if (hasReflexion && hasTransparency)
  {
    reflectance = getFresnelReflectance(data, ray);
    transmittance = 1.0 - reflectance;
  }

  if (hasReflexion)
    reflexion = handleReflection(scene, ray, data, depth) * reflectance;

  if (hasTransparency)
    transparency = handleTransparency(scene, ray, data, depth) * transmittance;

  return reflexion + transparency;
}

Tools::Refractは、単にglm::refractを内部的に呼び出しています。 (必要に応じて簡単に変更できるように)

n1n2の概念は扱いません:n2は常に空気に対して1と見なされます。

私は明らかな何かを見逃していますか?


[〜#〜]編集[〜#〜]

光線がオブジェクトの内部にあるかどうかを知る方法を追加した後(そしてそうであれば法線を否定した後)、私はこれを持っています:

Refraction problem

助けを求めて周りを見回しているときに、私は this postに出くわしましたが、答えは何も答えないと思います。それを読んでも、何をすべきか全くわかりません。


編集2

私はたくさんのことを試しました、そして私は現在この時点にいます:

Current

それは良いですが、それが正しいかどうかはまだわかりません。私はこの画像をインスピレーションとして使用しています:

Example

しかし、これは2つの屈折率(現実に近づけるため)を使用していますが、単純化し、常に空気を2番目の(インまたはアウト)マテリアルと見なしたいと思います。

私がコードで本質的に変更したのはここにあります:

inline Vec_t Refract(Vec_t v, const IntersectionData& data, Float_t eta)
{
  Float_t n = eta;

  if (data.isInside)
    n = 1.0 / n;
  double cosI = Tools::DotProduct(v, data.normal);

  return v * n - data.normal * (-cosI + n * cosI);
}

これが同じ球の別のビューです:

Spheres

22
Telokis

編集:私はこれの前のバージョンが完全に正しくなかったと思ったので、私は答えを編集します。

すべてのコメント、質問の新しいバージョンを読み、自分でいくつかの実験を行った後、次のバージョンのrefractルーチンを作成しました。

float3 refract(float3 i, float3 n, float eta)
{
    eta = 2.0f - eta;
    float cosi = dot(n, i);
    float3 o = (i * eta - n * (-cosi + eta * cosi));
    return o;
}

今回はそれを呼び出すために追加の操作は必要ありません。

float3 refr = refract(rayDirection, normal, refrIdx);

私がまだ確信が持てない唯一のことは、内部光線の交差を行うときの屈折率の反転です。私のテストでは、インデックスを反転したかどうかに関係なく、生成された画像に大きな違いはありませんでした。

インデックスが異なるいくつかの画像の下:

enter image description hereenter image description here

その他の画像については、 リンク を参照してください。このサイトでは、ここに画像を追加することは許可されていません。

6
Matso

私はこれにプログラマーではなく物理学者として答えています。すべてのコードを読む時間がなかったので、一般的な考えだけを修正するためのコードを提供するつもりはありません。

上で述べたことから、黒いリングはn_objectがn_airよりも小さい場合のものです。これは通常、オブジェクトの中にいる場合にのみ当てはまります。たとえば、水中にいる場合などですが、マテリアルはそのような奇妙なプロパティで構築されているため、サポートする必要があります。

このタイプの状況では、回折式が材料間の界面の同じ側に屈折光線を置くため、回折できない光線がありますが、これは明らかに回折として意味がありません。この状況では、代わりにサーフェスは反射サーフェスのように機能します。これは、しばしば全反射と呼ばれる状況です。

完全に正確である場合、ほとんどすべての屈折物体も部分的に反射し、反射または透過する(したがって屈折する)光の割合は フレネルの式 で与えられます。この場合でも、角度が遠すぎる場合は反射性であり、そうでない場合は透過性(したがって屈折性)であると見なすのが適切な近似です。

また、反射が不可能な場合(これらの方向が暗いため)、この黒いリング効果が見られる場合がありますが、透過される光は可能です。これは、オブジェクトのエッジにぴったりとフィットし、直接離れて向けられ、チューブの外側ではなく内側にのみ光を当てるカードのチューブを取ることによって行うことができます。

3
staircase27