指向派生类对象的指针丢失 vfptr

发布于 2024-08-30 16:44:59 字数 4931 浏览 7 评论 0原文

首先,我尝试编写一个普通的、简单的光线追踪器。在我的光线追踪器中,世界上有多种类型的几何图形,所有这些都派生自名为“SceneObject”的基类。我已经在此处包含了它的标题。

/**
Interface for all objects that will appear in a scene
*/
class SceneObject
{
public:
 mat4 M, M_inv;
 Color c;

 SceneObject();
 ~SceneObject();

 /**
 The transformation matrix to be applied to all points
 of this object.  Identity leaves the object in world frame.
 */
 void setMatrix(mat4 M);
 void setMatrix(MatrixStack mStack);
 void getMatrix(mat4& M);

 /**
 The color of the object
 */
 void setColor(Color c);
 void getColor(Color& c);

 /**
 Alter one portion of the color, leaving
 the rest as they were.
 */
 void setDiffuse(vec3 rgb);
 void setSpecular(vec3 rgb);
 void setEmission(vec3 rgb);
 void setAmbient(vec3 rgb);
 void setShininess(double s);

 /** 
 Fills 'inter' with information regarding an intersection between
 this object and 'ray'.  Ray should be in world frame.
 */
 virtual void intersect(Intersection& inter, Ray ray) = 0;

 /**
 Returns a copy of this SceneObject
 */
 virtual SceneObject* clone()  = 0;

 /**
 Print information regarding this SceneObject for debugging
 */
 virtual void print() = 0;
};

正如您所看到的,我已经包含了几个要在其他地方实现的虚拟函数。在本例中,我只有两个派生类——Sphere 和 Triangle,它们都实现了缺少的成员函数。最后,我有一个 Parser 类,其中充满了执行实际“光线跟踪”部分的静态方法。这是相关部分的几个片段

void Parser::trace(Camera cam, Scene scene, string outputFile, int maxDepth) {
 int width = cam.getNumXPixels();
 int height = cam.getNumYPixels();
 vector<vector<vec3>> colors;
 colors.clear();
 for (int i = 0; i< width; i++) {
  vector<vec3> ys;
  for (int j = 0; j<height; j++) {
   Intersection intrsct; 
   Ray ray; cam.getRay(ray, i, j);
   vec3 color;
   printf("Obtaining color for Ray[%d,%d]\n", i,j);
   getColor(color, scene, ray, maxDepth);
   ys.push_back(color);
  }
  colors.push_back(ys);
 }
 printImage(colors, width, height, outputFile);
}

void Parser::getColor(vec3& color, Scene scene, Ray ray, int numBounces)
{
 Intersection inter; scene.intersect(inter,ray);
 if(inter.isIntersecting()){
  Color c; inter.getColor(c);
  c.getAmbient(color);
 } else {
  color = vec3(0,0,0);
 }
}

现在,我已经放弃了真正的光线追踪部分,而是简单地返回第一个命中对象的颜色(如果有)。您无疑已经注意到,计算机知道光线与对象相交的唯一方法是通过 Scene.intersect(),我也将其包括在内。使用的成员变量是“向量对象”见结束

好了,现在来说说问题。我首先创建一个场景并用 Parser::trace() 方法之外的对象填充它。现在,出于某种奇怪的原因,我将 Ray 投射为 i=j=0,一切都运行良好。然而,当第二条光线投射时,场景中存储的所有对象都不再识别它们的 vfptr(也就是说,我仍然可以访问除虚拟对象之外的所有 SceneObject 方法)!我使用调试器单步执行代码,发现所有 vfptr 的信息在 getColor() 结束和循环继续之间的某个位置丢失了。但是,如果我更改 getColor() 的参数以使用 Scene&而不是场景,那么就不会发生丢失。这是什么疯狂的巫术?

根据要求,场景代码:

#include <vector>
#include <limits>
#include "Intersection.h"
#include "LightSource.h"
#include "SceneObject.h"

using namespace std;

/**
Contains a list of scene objects.  A ray can be 
intersected with a scene to find its color
*/
class Scene
{
public:
    vector<SceneObject*> objects;
    vector<LightSource*> lights;

    Scene(void);
    ~Scene(void);

    /** 
    Add an object to the scene
    */
    void addObject(SceneObject& o);

    /**
    Add a light source to the scene
    */
    void addLight(LightSource& l);

    /**
    Fill 'l' with all light sources in the scene
    */
    void getLightSources(vector<LightSource*>& l);

    /**
    Fills 'i' with information regarding an
    intersection with the closest object in the scene
    IF there is an intersection.  Check i.isIntersecting()
    to see if an intersection was actually found.
    */
    void intersect(Intersection& i, Ray r);

    void print();
};

#include "Scene.h"

Scene::Scene(void)
{
}

Scene::~Scene(void)
{
    for(int i=0;i<objects.size();i++){
        delete objects[i];
    }
    for(int i=0;i<lights.size();i++){
        delete lights[i];
    }
}

void Scene::addObject(SceneObject& o)
{
    objects.push_back(o.clone());
}

void Scene::addLight(LightSource& l)
{
    lights.push_back(l.clone());
}

void Scene::getLightSources(vector<LightSource*>& l)
{
    l = lights;
}

void Scene::intersect(Intersection& i, Ray r)
{
    Intersection result;
    result.setDistance(numeric_limits<double>::infinity());
    result.setIsIntersecting(false);

    double oldDist; result.getDistance(oldDist);

    /* Cycle through all objects, making result
    the closest one */
    for(int ind=0; ind<objects.size(); ind++){
        SceneObject* thisObj = objects[ind];
        Intersection betterIntersect;
        thisObj->intersect(betterIntersect, r);

        double newDist; betterIntersect.getDistance(newDist);
        if (newDist < oldDist){
            result = betterIntersect;
            oldDist = newDist;
        }
    }

    i = result;
}

void Scene::print()
{
    printf("%d Objects:\n", objects.size());
    for(int i=0;i<objects.size();i++){
        objects[i]->print();
    }
}

To begin, I am trying to write a run-of-the-mill, simple Ray Tracer. In my Ray Tracer, I have multiple types of geometries in the world, all derived from a base class called "SceneObject". I've included the header for it here.

/**
Interface for all objects that will appear in a scene
*/
class SceneObject
{
public:
 mat4 M, M_inv;
 Color c;

 SceneObject();
 ~SceneObject();

 /**
 The transformation matrix to be applied to all points
 of this object.  Identity leaves the object in world frame.
 */
 void setMatrix(mat4 M);
 void setMatrix(MatrixStack mStack);
 void getMatrix(mat4& M);

 /**
 The color of the object
 */
 void setColor(Color c);
 void getColor(Color& c);

 /**
 Alter one portion of the color, leaving
 the rest as they were.
 */
 void setDiffuse(vec3 rgb);
 void setSpecular(vec3 rgb);
 void setEmission(vec3 rgb);
 void setAmbient(vec3 rgb);
 void setShininess(double s);

 /** 
 Fills 'inter' with information regarding an intersection between
 this object and 'ray'.  Ray should be in world frame.
 */
 virtual void intersect(Intersection& inter, Ray ray) = 0;

 /**
 Returns a copy of this SceneObject
 */
 virtual SceneObject* clone()  = 0;

 /**
 Print information regarding this SceneObject for debugging
 */
 virtual void print() = 0;
};

As you can see, I've included a couple virtual functions to be implemented elsewhere. In this case, I have only two derived class -- Sphere and Triangle, both of which implement the missing member functions. Finally, I have a Parser class, which is full of static methods that do the actual "Ray Tracing" part. Here's a couple snippets for relevant portions

void Parser::trace(Camera cam, Scene scene, string outputFile, int maxDepth) {
 int width = cam.getNumXPixels();
 int height = cam.getNumYPixels();
 vector<vector<vec3>> colors;
 colors.clear();
 for (int i = 0; i< width; i++) {
  vector<vec3> ys;
  for (int j = 0; j<height; j++) {
   Intersection intrsct; 
   Ray ray; cam.getRay(ray, i, j);
   vec3 color;
   printf("Obtaining color for Ray[%d,%d]\n", i,j);
   getColor(color, scene, ray, maxDepth);
   ys.push_back(color);
  }
  colors.push_back(ys);
 }
 printImage(colors, width, height, outputFile);
}

void Parser::getColor(vec3& color, Scene scene, Ray ray, int numBounces)
{
 Intersection inter; scene.intersect(inter,ray);
 if(inter.isIntersecting()){
  Color c; inter.getColor(c);
  c.getAmbient(color);
 } else {
  color = vec3(0,0,0);
 }
}

Right now, I've forgone the true Ray Tracing part and instead simply return the color of the first object hit, if any. As you have no doubt noticed, the only way the computer knows that a ray has intersected an object is through Scene.intersect(), which I also include. The member variable used is "vector objects" see end

Alright, now for the problem. I begin by creating a scene and filling it with objects outside of the Parser::trace() method. Now for some odd reason, I cast Ray for i=j=0 and everything works wonderfully. However, by the time the second ray is cast all of the objects stored in my Scene no longer recognize their vfptr's (that is to say, I can still access all the SceneObject methods excepting those that are virtual)! I stepped through the code with a debugger and found that the information to all the vfptr's are lost somewhere between the end of getColor() and the continuation of the loop. However, if I change the arguments of getColor() to use a Scene& instead of a Scene, then no loss occurs. What crazy voodoo is this?

Code for Scene, as requested:

#include <vector>
#include <limits>
#include "Intersection.h"
#include "LightSource.h"
#include "SceneObject.h"

using namespace std;

/**
Contains a list of scene objects.  A ray can be 
intersected with a scene to find its color
*/
class Scene
{
public:
    vector<SceneObject*> objects;
    vector<LightSource*> lights;

    Scene(void);
    ~Scene(void);

    /** 
    Add an object to the scene
    */
    void addObject(SceneObject& o);

    /**
    Add a light source to the scene
    */
    void addLight(LightSource& l);

    /**
    Fill 'l' with all light sources in the scene
    */
    void getLightSources(vector<LightSource*>& l);

    /**
    Fills 'i' with information regarding an
    intersection with the closest object in the scene
    IF there is an intersection.  Check i.isIntersecting()
    to see if an intersection was actually found.
    */
    void intersect(Intersection& i, Ray r);

    void print();
};

#include "Scene.h"

Scene::Scene(void)
{
}

Scene::~Scene(void)
{
    for(int i=0;i<objects.size();i++){
        delete objects[i];
    }
    for(int i=0;i<lights.size();i++){
        delete lights[i];
    }
}

void Scene::addObject(SceneObject& o)
{
    objects.push_back(o.clone());
}

void Scene::addLight(LightSource& l)
{
    lights.push_back(l.clone());
}

void Scene::getLightSources(vector<LightSource*>& l)
{
    l = lights;
}

void Scene::intersect(Intersection& i, Ray r)
{
    Intersection result;
    result.setDistance(numeric_limits<double>::infinity());
    result.setIsIntersecting(false);

    double oldDist; result.getDistance(oldDist);

    /* Cycle through all objects, making result
    the closest one */
    for(int ind=0; ind<objects.size(); ind++){
        SceneObject* thisObj = objects[ind];
        Intersection betterIntersect;
        thisObj->intersect(betterIntersect, r);

        double newDist; betterIntersect.getDistance(newDist);
        if (newDist < oldDist){
            result = betterIntersect;
            oldDist = newDist;
        }
    }

    i = result;
}

void Scene::print()
{
    printf("%d Objects:\n", objects.size());
    for(int i=0;i<objects.size();i++){
        objects[i]->print();
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

柠北森屋 2024-09-06 16:44:59

问题是您在 Scene 的析构函数中删除了 SceneObjects 并使用了默认的复制构造函数,该构造函数使用指针向量进行平面复制。这意味着,Scene 的每个副本都引用相同的 SceneObjects。如果这些Scene之一被销毁,它们都会丢失其引用的对象。如果您通过引用传递场景,这没有问题,因为在这种情况下不会创建副本并随后销毁。

The problem is that you delete your SceneObjects in the destructor of Scene and you use the default copy-constructor, which does flat copy with a vector of pointers. That means, each copy of a Scene references the same SceneObjects. If one of these Scenes is destryed, they all lose their referred objects. This is no problem if you pass scene by reference, since in that case no copy is made and later destroyed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文