Указатели на производные объекты класса Losing vfptr - PullRequest
1 голос
/ 25 апреля 2010

Для начала я пытаюсь написать простой заурядный Ray Tracer. В моем Ray Tracer у меня есть несколько типов геометрий в мире, все они получены из базового класса, называемого «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 (), который я также включил. Используемая переменная-член: "векторные объекты" см. End

Хорошо, теперь для проблемы. Я начинаю с создания сцены и заполнения ее объектами вне метода Parser :: trace (). Теперь по какой-то странной причине я разыграл Ray для i = j = 0, и все прекрасно работает. Однако к тому моменту, когда второй луч приведен в действие, все объекты, хранящиеся в моей сцене, больше не распознают их vfptr (то есть я все еще могу получить доступ ко всем методам SceneObject, кроме виртуальных)! Я прошел по коду с помощью отладчика и обнаружил, что информация для всех vfptr потеряна где-то между концом getColor () и продолжением цикла. Однако, если я изменю аргументы getColor (), чтобы использовать Scene & вместо 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();
    }
}

1 Ответ

5 голосов
/ 25 апреля 2010

Проблема в том, что вы удаляете свой SceneObjects в деструкторе Scene и используете конструктор копирования по умолчанию, который выполняет плоское копирование с вектором указателей. Это означает, что каждая копия Scene ссылается на один и тот же SceneObjects. Если один из этих Scene s уничтожен, все они теряют свои упомянутые объекты. Это не проблема, если вы передаете сцену по ссылке, так как в этом случае копия не создается, а затем уничтожается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...