2

私は単純なレイ トレーサを作成していますが、今のところは単純にするために、シーンに球だけを使用することにしました。私は今、光線がシーン内の球体と適切に交差していることを確認したいだけの段階にいます。Ray と Sphere クラスを作成し、メイン ファイルに関数を作成して、各ピクセルを調べて交差があるかどうかを確認しました (関連するコードを以下に掲載します)。問題は、球との交点全体がかなり奇妙な動作をしていることです。中心 (0, 0, -20) で半径 1 の球を作成すると、常にイメージの最初のピクセル (左上隅) にある交点が 1 つだけ得られます。半径 15 に達すると、左上の領域に突然 3 つの交差点ができます。

ここで光線と球体の交差コードに問題があるのではないかと疑っていましたが、それを調べてネットで詳細を調べたところ、ほとんどのソリューションは私が使用するのとまったく同じアプローチを説明しているので、問題があるべきではないと思います(!)ここ。だから...何が間違っているのか正確にはわかりません。交差点コードであるか、問題の原因である可能性があります。私はそれを見つけることができないようです。球と光線に値を与えるとき、私の考えが間違っているのでしょうか? 以下は関連するコードです

球体クラス:

Sphere::Sphere(glm::vec3 center, float radius) 
: m_center(center), m_radius(radius), m_radiusSquared(radius*radius)
{
}

//Sphere-ray intersection. Equation: (P-C)^2 - R^2 = 0, P = o+t*d
//(P-C)^2 - R^2 => (o+t*d-C)^2-R^2 => o^2+(td)^2+C^2+2td(o-C)-2oC-R^2
//=> at^2+bt+c, a = d*d, b = 2d(o-C), c = (o-C)^2-R^2
//o = ray origin, d = ray direction, C = sphere center, R = sphere radius
bool Sphere::intersection(Ray& ray) const
{
    //Squared distance between ray origin and sphere center
    float squaredDist = glm::dot(ray.origin()-m_center, ray.origin()-m_center);

    //If the distance is less than the squared radius of the sphere...
    if(squaredDist <= m_radiusSquared)
    {
        //Point is in sphere, consider as no intersection existing
        //std::cout << "Point inside sphere..." << std::endl;
        return false;
    }

    //Will hold solution to quadratic equation
    float t0, t1;

    //Calculating the coefficients of the quadratic equation
    float a = glm::dot(ray.direction(),ray.direction()); // a = d*d
    float b = 2.0f*glm::dot(ray.direction(),ray.origin()-m_center); // b = 2d(o-C)
    float c = glm::dot(ray.origin()-m_center, ray.origin()-m_center) - m_radiusSquared; // c = (o-C)^2-R^2

    //Calculate discriminant
    float disc = (b*b)-(4.0f*a*c);

    if(disc < 0) //If discriminant is negative no intersection happens
    {
        //std::cout << "No intersection with sphere..." << std::endl;
        return false;
    }
    else //If discriminant is positive one or two intersections (two solutions) exists
    {
        float sqrt_disc = glm::sqrt(disc);
        t0 = (-b - sqrt_disc) / (2.0f * a);
        t1 = (-b + sqrt_disc) / (2.0f * a);
    }

    //If the second intersection has a negative value then the intersections
    //happen behind the ray origin which is not considered. Otherwise t0 is
    //the intersection to be considered
    if(t1<0)
    {
        //std::cout << "No intersection with sphere..." << std::endl;
        return false;
    }
    else
    {
        //std::cout << "Intersection with sphere..." << std::endl;
        return true;
    }
}

プログラム:

#include "Sphere.h"
#include "Ray.h"

void renderScene(const Sphere& s);

const int imageWidth = 400;
const int imageHeight = 400;

int main()
{
    //Create sphere with center in (0, 0, -20) and with radius 10
    Sphere testSphere(glm::vec3(0.0f, 0.0f, -20.0f), 10.0f);

    renderScene(testSphere);

    return 0;
}

//Shoots rays through each pixel and check if there's an intersection with
//a given sphere. If an intersection exists then the counter is increased.
void renderScene(const Sphere& s)
{
    //Ray r(origin, direction)
    Ray r(glm::vec3(0.0f), glm::vec3(0.0f));

    //Will hold the total amount of intersections
    int counter = 0;

    //Loops through each pixel...
    for(int y=0; y<imageHeight; y++)
    {
        for(int x=0; x<imageWidth; x++)
        {
            //Change ray direction for each pixel being processed
            r.setDirection(glm::vec3(((x-imageWidth/2)/(float)imageWidth), ((imageHeight/2-y)/(float)imageHeight), -1.0f));

            //If current ray intersects sphere...
            if(s.intersection(r))
            {
                //Increase counter
                counter++;
            }
        }
    }

    std::cout << counter << std::endl;
}
4

2 に答える 2

2

二次方程式の2 番目の解 ( ) は、次のようなものが必要なt1ケースでは間違っています。disc > 0

float sqrt_disc = glm::sqrt(disc);
t0 = (-b - sqrt_disc) / (2 * a);
t1 = (-b + sqrt_disc) / (2 * a);

コードが数学に似ているほど、チェックが容易になるため、2 で除算して 0.5 を乗算するよりも、この形式で式を書き出すのが最善だと思います。

その他のいくつかのマイナーなコメント:

  1. discの名前を再利用するのは紛らわしいsqrt(disc)ので、上記の新しい変数名を使用しました。

  2. との両方が正であり、常に よりも大きいt0 > t1ことがわかっているため、 をテストする必要はありません。asqrt_disct1t0

  3. 光線の原点が球の内側にある場合、t0が負t1になることも正になることもあります。あなたはこの事件を扱っていないようです。

  4. disc == 0一般的なケースは特別なケースと同じ値を計算するため、 の特別なケースは必要ありません。(また、特殊なケースが少ないほど、コードのチェックが容易になります。)

于 2012-10-01T17:54:13.683 に答える
1

私があなたのコードを正しく理解しているなら、次のことを試してみてください:

r.setDirection(glm::vec3(((x-imageWidth/2)/(float)imageWidth),
                         ((imageHeight/2-y)/(float)imageHeight),
                         -1.0f));

現時点では、カメラを画面から 1 単位離れた位置に配置していますが、光線は左右に 400 単位も発射できます。これは非常に広い視野です。また、光線は 1 オクテントの空間しか掃引していません。これが、画面の左上隅にほんの一握りのピクセルしか表示されない理由です。上で書いたコードはそれを修正するはずです。

于 2012-10-01T18:35:03.700 に答える