【问题标题】:Grainy looking sphere in my ray tracer我的光线追踪器中的颗粒状球体
【发布时间】:2015-09-03 16:35:08
【问题描述】:

我正在尝试编写一个简单的光线追踪器。最终图像应该是这样的: 我已经阅读了有关它的内容,下面是我正在做的事情:

 create an empty image (to fill each pixel, via ray tracing)
 for each pixel [for each row, each column]
 create the equation of the ray emanating from our pixel
 trace() ray:
 if ray intersects SPHERE
 compute local shading (including shadow determination)
 return color;

现在,场景数据是这样的:它在 (0,0,-3) 处设置了一个半径为 1 的灰色球体。它在原点设置了一个白光源。

 2
 amb: 0.3 0.3 0.3
 sphere
 pos: 0.0 0.0 -3.0
 rad: 1
 dif: 0.3 0.3 0.3
 spe: 0.5 0.5 0.5
 shi: 1
 light
 pos: 0 0 0
 col: 1 1 1

我的看起来很奇怪:

//check ray intersection with the sphere
boolean intersectsWithSphere(struct point rayPosition, struct point rayDirection,    Sphere sp,float* t){

//float a = (rayDirection.x * rayDirection.x) + (rayDirection.y * rayDirection.y) +(rayDirection.z * rayDirection.z);
// value for a is 1 since rayDirection vector is normalized
double radius = sp.radius;
double xc = sp.position[0];
double yc =sp.position[1];
double zc =sp.position[2];

double xo = rayPosition.x;
double yo = rayPosition.y;
double zo = rayPosition.z;

double xd = rayDirection.x;
double yd = rayDirection.y;
double zd = rayDirection.z;

double b = 2 * ((xd*(xo-xc))+(yd*(yo-yc))+(zd*(zo-zc)));
double c = (xo-xc)*(xo-xc) + (yo-yc)*(yo-yc) + (zo-zc)*(zo-zc) - (radius * radius);
float D = b*b + (-4.0f)*c;

//ray does not intersect the sphere
if(D < 0 ){
    return false;
}

D = sqrt(D);
float t0 = (-b - D)/2 ;
float t1 = (-b + D)/2;

//printf("D=%f",D);
//printf(" t0=%f",t0);
//printf(" t1=%f\n",t1);

if((t0 > 0) && (t1 > 0)){
    *t = min(t0,t1);
    return true;
}
else {
    *t = 0;
    return false;
}

}

下面是trace()函数:

unsigned char* trace(struct point rayPosition, struct point rayDirection, Sphere * totalspheres) {

struct point tempRayPosition = rayPosition;
struct point tempRayDirection = rayDirection;
float  f=0;
float tnear = INFINITY;
boolean sphereIntersectionFound = false;
int sphereIndex = -1;
for(int i=0; i < num_spheres ; i++){
    float t = INFINITY;
    if(intersectsWithSphere(tempRayPosition,tempRayDirection,totalspheres[i],&t)){
        if(t < tnear){
            tnear = t;
            sphereIntersectionFound = true;
            sphereIndex = i;
        }
    }
}

if(sphereIndex < 0){
    //printf("No interesection found\n");
    mycolor[0] = 1;
    mycolor[1] = 1;
    mycolor[2] = 1;
    return mycolor;
}
else {
       Sphere sp = totalspheres[sphereIndex];
        //intersection point
        hitPoint[0].x = tempRayPosition.x + tempRayDirection.x * tnear;
        hitPoint[0].y = tempRayPosition.y + tempRayDirection.y * tnear;
        hitPoint[0].z = tempRayPosition.z + tempRayDirection.z * tnear;

        //normal at the intersection point
        normalAtHitPoint[0].x = (hitPoint[0].x - totalspheres[sphereIndex].position[0])/ totalspheres[sphereIndex].radius;
        normalAtHitPoint[0].y = (hitPoint[0].y - totalspheres[sphereIndex].position[1])/ totalspheres[sphereIndex].radius;
        normalAtHitPoint[0].z = (hitPoint[0].z - totalspheres[sphereIndex].position[2])/ totalspheres[sphereIndex].radius;
        normalizedNormalAtHitPoint[0] = normalize(normalAtHitPoint[0]);

        for(int j=0; j < num_lights ; j++) {

            for(int k=0; k < num_spheres ; k++){

                shadowRay[0].x = lights[j].position[0] - hitPoint[0].x;
                shadowRay[0].y = lights[j].position[1] - hitPoint[0].y;
                shadowRay[0].z = lights[j].position[2] - hitPoint[0].z;
                normalizedShadowRay[0]  = normalize(shadowRay[0]);

                //R = 2 * ( N dot L) * N - L
                reflectionRay[0].x = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].x +normalizedShadowRay[0].x;
                reflectionRay[0].y = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].y +normalizedShadowRay[0].y;
                reflectionRay[0].z = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].z +normalizedShadowRay[0].z;
                normalizeReflectionRay[0] = normalize(reflectionRay[0]);

                        struct point temp;
                        temp.x = hitPoint[0].x + (shadowRay[0].x * 0.0001 );
                        temp.y = hitPoint[0].y + (shadowRay[0].y * 0.0001);
                        temp.z = hitPoint[0].z + (shadowRay[0].z * 0.0001);

                        struct point ntemp = normalize(temp);
                        float f=0;
                        struct point tempHitPoint;
                        tempHitPoint.x = hitPoint[0].x + 0.001;
                        tempHitPoint.y = hitPoint[0].y + 0.001;
                        tempHitPoint.z = hitPoint[0].z + 0.001;

                        if(intersectsWithSphere(hitPoint[0],ntemp,totalspheres[k],&f)){
                        //  if(intersectsWithSphere(tempHitPoint,ntemp,totalspheres[k],&f)){
                            printf("In shadow\n");
                            float r = lights[j].color[0];                       
                            float g = lights[j].color[1];
                            float b = lights[j].color[2];
                            mycolor[0] = ambient_light[0] + r;
                            mycolor[1] = ambient_light[1] + g;
                            mycolor[2] = ambient_light[2] + b;
                            return mycolor;

                    } else {

                        // point is not is shadow , use Phong shading to determine the color of the point.      
                        //I = lightColor * (kd * (L dot N) + ks * (R dot V) ^ sh)       
                        //(for each color channel separately; note that if L dot N < 0, you should clamp L dot N to zero; same for R dot V)

                       float x = dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]);
                       if(x < 0)
                           x = 0;

                       V[0].x = - rayDirection.x;
                       V[0].x = - rayDirection.y;
                       V[0].x = - rayDirection.z;
                       normalizedV[0] = normalize(V[0]);
                       float y = dot(normalizeReflectionRay[0],normalizedV[0]);
                       if(y < 0)
                           y = 0;
                       float ar = totalspheres[sphereIndex].color_diffuse[0] * x;
                       float br =  totalspheres[sphereIndex].color_specular[0] * pow(y,totalspheres[sphereIndex].shininess);
                       float r = lights[j].color[0] * (ar+br);
                    //----------------------------------------------------------------------------------
                       float bg =  totalspheres[sphereIndex].color_specular[1] * pow(y,totalspheres[sphereIndex].shininess);
                       float ag = totalspheres[sphereIndex].color_diffuse[1] * x;
                       float g = lights[j].color[1] * (ag+bg);
                    //----------------------------------------------------------------------------------
                       float bb =  totalspheres[sphereIndex].color_specular[2] * pow(y,totalspheres[sphereIndex].shininess);
                       float ab = totalspheres[sphereIndex].color_diffuse[2] * x;
                       float b = lights[j].color[2] * (ab+bb);
                        mycolor[0] =  r + ambient_light[0];
                        mycolor[1] =  g + ambient_light[1];
                        mycolor[2] =  b+ ambient_light[2];
                        return mycolor;
                        } 
                }
        }
    }           
}

调用trace() 的代码如下:

void draw_scene()
{
//Aspect Ratio
double a = WIDTH / HEIGHT;
double angel = tan(M_PI * 0.5 * fov/ 180);
ray[0].x = 0.0;
ray[0].y = 0.0;
ray[0].z = 0.0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
unsigned int x,y;
float sx, sy;
for(x=0;x < WIDTH;x++)
{
    glPointSize(2.0); 
    glBegin(GL_POINTS);
    for(y=0;y < HEIGHT;y++)
    {
        sx = (((x + 0.5) / WIDTH) * 2.0 ) - 1;
        sy = (((y + 0.5) / HEIGHT) * 2.0 ) - 1;;
        sx  = sx * angel * a;
        sy = sy * angel;
        //set ray direction
        ray[1].x = sx;
        ray[1].y = sy;
        ray[1].z = -1;
        normalizedRayDirection[0] = normalize(ray[1]);
        unsigned char* color = trace(ray[0],normalizedRayDirection[0],spheres);
        unsigned char  x1 = color[0] * 255;
        unsigned char  y1 = color[1] * 255;
        unsigned char  z1 = color[2] * 255;
        plot_pixel(x,y,x1 %256,y1%256,z1%256);
    }
   glEnd();
   glFlush();
 }
}

代码/理解可能存在很多很多问题。

【问题讨论】:

  • 如果有命中尝试只打印黑色,如果没有命中则尝试打印白色。所以我们可以看到结果并从那里得到它。
  • 另外,不要使用GL_POINTS 原语来覆盖 2x2 像素四边形。实现只需要支持 1.0 的最小和最大点大小,并且GL_POINTS 并不总是正方形,具体取决于某些渲染状态。您可以使用 glDrawPixels (...) 轻松绘制 2x2 正方形,这是可以接受的做法,因为您使用的是已弃用的东西,例如立即模式。
  • @Guido:不走运,我按照你的建议做了。由于光泽而突出的亮点被删除,其余都是一样的
  • @Margi 那么说明intersectsWithSphere函数有问题!我现在会更仔细地研究它。

标签: c opengl raytracing


【解决方案1】:

我没有花时间看懂你所有的代码,而且我绝对不是图形专家,但我相信你遇到的问题叫做“表面痤疮”。在这种情况下,它可能会发生,因为您的阴影光线与对象本身相交。我在代码中解决此问题的方法是将epsilon * hitPoint.normal 添加到阴影射线原点。这有效地将光线从您的对象移开一点,因此它们不会相交。

我用于 epsilon 的值是 1.19209290 * 10^-7 的平方根,因为它是一个名为 EPSILON 的常量的平方根,该常量以我使用的特定语言定义。

【讨论】:

    【解决方案2】:

    你这样做有什么可能的原因(在trace (...)的非影子分支中):

    V[0].x = - rayDirection.x;
    V[0].x = - rayDirection.y;
    V[0].x = - rayDirection.z;
    

    您不妨注释掉前两个计算,因为您将每个计算的结果写入同一个组件。我认为您可能打算这样做:

    V[0].x = - rayDirection.x;
    V[0].y = - rayDirection.y;
    V[0].z = - rayDirection.z;
    

    也就是说,您还应该避免使用 GL_POINT 基元来覆盖 2x2 像素四边形。点图元不保证是正方形的,OpenGL 实现不需要支持 1.0 以外的任何尺寸。在实践中,大多数支持 1.0 - ~64.0glDrawPixels (...) 是写入 2x2 像素的更好方法,因为它跳过了原始组装和上述限制。无论如何,您在此示例中使用的是立即模式,因此 glRasterPos (...)glDrawPixels (...) 仍然是有效的方法。

    【讨论】:

    • 谢谢,我没有意识到这个错误。修改了代码,仍然是相同的图像。我真的不明白为什么我不能使用 GL_POINT。正如我所说,我还是一个新手,这是我们应该按照讲师提供的启动代码使用的。
    • @Margi:啊。这是一种耻辱。我对GL_POINT 的反对意见只是不能保证在此示例代码中始终执行您要求它执行的操作。 OpenGL 的大多数实现将允许 2.0 的点大小,但任何 OpenGL 实现需要支持的最低限度实际上是 1.0。如果您启用了抗锯齿或点平滑,它们也不总是方形的。对于您的案例,您可能可以放心地忽略这些细节,但它们是需要注意的可移植性问题。
    【解决方案3】:

    您似乎正在执行公式here,但您最终偏离了文章所采用的方向。

    首先,文章警告说 D & b 的值可能非常接近,因此 -b + D 得到的数字非常有限。他们提出了一个替代方案。

    此外,您正在测试 t0 和 t1 > 0。这并不一定是您击中球体的情况,您可以在球体内部(尽管您显然不应该在测试场景中)。

    最后,我会在开头添加一个测试,以确认方向向量是否已归一化。我在渲染器中不止一次把它搞砸了。

    【讨论】:

    • 谢谢,我检查了文章,检查了规范化。似乎没有什么对我有用。
    猜你喜欢
    • 2016-06-14
    • 1970-01-01
    • 2017-03-26
    • 2019-11-29
    • 2019-11-29
    • 2018-05-31
    • 2020-04-13
    • 2015-12-24
    • 1970-01-01
    相关资源
    最近更新 更多