2010-04-25 60 views
1

首先,我想寫一個普通的,簡單的Ray Tracer。在我的Ray Tracer中,我有多種類型的幾何圖形,都是從一個名爲「SceneObject」的基類中派生出來的。我在這裏包含了標題。指向衍生類對象的指針丟失vfptr

/** 
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; 
}; 

正如你所看到的,我已經包含了幾個虛擬函數在其他地方實現。在這種情況下,我只有兩個派生類 - 球體和三角形,它們都實現了缺少的成員函數。最後,我有一個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()方法以外的對象填充它。現在出於某種奇怪的原因,我把雷放在i = j = 0的位置,一切都很好。但是,在第二次射線投射時,存儲在我的場景中的所有對象不再識別它們的vfptr(也就是說,我仍然可以訪問除虛擬場景以外的所有SceneObject方法)!我用調試器遍歷代碼,發現所有vfptr的信息都在getColor()結束和循環的延續之間丟失。但是,如果我更改getColor()的參數以使用場景&而不是場景,則不會發生任何損失。這是什麼瘋狂的巫術?

代碼場景,如要求:

#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

對不起 - 我完全無法理解。然而,我會觀察到你的基類需要一個虛擬析構函數。 – 2010-04-25 10:06:44

+1

這聽起來很像你在'Scene'的複製構造函數和/或析構函數中有不正確的代碼,但是沒有看到'Scene'的定義,這很難確定。 – 2010-04-25 10:10:46

+0

可能不相關,但請確保您的方法是「const」正確的(http://www.parashift.com/c++-faq-lite/const-correctness.html)。 – kennytm 2010-04-25 10:12:56

回答

5

的問題是,你在Scene析構函數刪除SceneObjects,並使用默認的拷貝構造函數,它不會簡單複製與指針的載體。這意味着,Scene的每個副本都引用相同的SceneObjects。如果其中一個Scene s被選中,他們都會丟失他們提及的對象。如果你通過引用傳遞場景,這是沒有問題的,因爲在那種情況下沒有複製並且隨後被銷燬。

+0

完美無瑕。謝謝! – duckworthd 2010-04-25 21:05:48

相關問題