草庐IT

c++ - 如果没有递归光线跟踪,反射和折射是不可能的吗?

coder 2024-02-20 原文

我正在使用GLSL计算着色器编写基于GPU的实时光线跟踪渲染器。到目前为止,它确实工作得很好,但是当涉及同时具有反射和折射时,我偶然发现了一个看似无法解决的问题。

我的逻辑告诉我,为了在一个物体(如玻璃)上产生反射和折射,射线必须分成两束,一束射线从表面反射,另一束射线穿过该表面。这些光线的最终颜色然后将基于某些功能进行组合,并最终用作光线所源自的像素的颜色。我的问题是我无法在着色器代码中拆分光线,因为我必须使用递归来做到这一点。根据我的理解,着色器中的函数不能递归,因为由于与旧GPU硬件的兼容性问题,所有GLSL函数都类似于C++中的内联函数。

是否可以在着色器代码中模拟或伪造递归,或者甚至可以完全不使用递归而同时实现反射和折射?我看不到没有递归怎么办,但是我可能是错的。

最佳答案

我设法用我在注释中建议的方法将后向光线跟踪转换为适合 GLSL 的迭代过程。它远未优化,我还没有实现所有物理方面的东西(没有斯涅尔定律等),但是作为概念证明,它已经起作用。我在片段着色器和 CPU 边代码中完成所有工作,只是以 32位非固定 float 纹理 uniforms的形式发送GL_LUMINANCE32F_ARB常量和场景。渲染只是覆盖整个屏幕的单个QUAD

  • 通过场景

    我决定将场景存储在纹理中,以便每个射线/片段都可以直接访问整个场景。纹理为 2D ,但它用作32位浮点数的线性列表。我决定这种格式:
    enum _fac_type_enum
        {
        _fac_triangles=0,   // r,g,b,a, n, triangle count, { x0,y0,z0,x1,y1,z1,x2,y2,z2 }
        _fac_spheres,       // r,g,b,a, n, sphere count,   { x,y,z,r }
        };
    const GLfloat _n_glass=1.561;
    const GLfloat _n_vacuum=1.0;
    GLfloat data[]=
        {
    //    r,  g,  b,  a,       n,          type,count
        0.2,0.3,0.5,0.5,_n_glass,_fac_triangles,    4,      // tetrahedron
    //        px,  py,  pz,  r,  g,  b
            -0.5,-0.5,+1.0,
             0.0,+0.5,+1.0,
            +0.5,-0.5,+1.0,
    
             0.0, 0.0,+0.5,
            -0.5,-0.5,+1.0,
             0.0,+0.5,+1.0,
    
             0.0, 0.0,+0.5,
             0.0,+0.5,+1.0,
            +0.5,-0.5,+1.0,
    
             0.0, 0.0,+0.5,
            +0.5,-0.5,+1.0,
            -0.5,-0.5,+1.0,
        }; 
    

    您可以添加/更改任何类型的对象。此示例仅包含一个半透明的蓝色四面体。您还可以为 Material 属性等添加变换矩阵更多的系数...
  • 建筑

    顶点着色器仅初始化插值的 View 的角光线(起始位置和方向),因此每个片段都表示反向光线跟踪过程的起始光线。

  • 迭代反向光线跟踪

    因此,我创建了一个“静态”光线列表,并使用起始光线对其进行了初始化。迭代过程分两个步骤,首先是反向光线跟踪:
  • 从第一个
  • 列表中循环遍历所有光线
  • 查找与场景最近的交点 ...

    将位置,表面法线和 Material 属性存储到ray struct
  • 如果找到相交点而不是最后一个“递归”层,则将反射/折射光线添加到列表的末尾。

    还将它们的索引存储到已处理的ray struct

  • 现在,您的光线应包含重建颜色所需的所有相交信息。要做到这一点:
  • 在所有递归级别中向后循环
  • ,用于匹配实际递归层的每条光线
  • 计算射线颜色

    因此,请使用所需的照明方程式。如果光线中包含子项,则根据 Material 属性(反射系数和折射系数...)将其颜色添加到结果中

  • 现在,第一条光线应该包含您要输出的颜色。

    使用的制服:

    tm_eye查看相机矩阵
    aspect View ys/xs纵横比
    n0空空间折射率(尚未使用)
    focal_length相机焦距
    场景正方形纹理的fac_siz分辨率
    场景纹理中实际使用的浮点数的fac_num 场景纹理的fac_txr纹理单元

    预览:



    片段着色器包含我的调试打印,因此如果需要,还需要纹理,请参见质量检查:
  • GLSL debug prints

  • 待办事项:

    添加对象,相机等的矩阵
    添加 Material 特性(光泽度,反射/折射系数)
    斯涅尔定律现在新射线的方向是错误的...
    可以将 R,G,B 分别设置为3个起始光线,并在末尾组合
    SSS 基于射线长度的地下散射
    更好地实现灯光(现在它们在代码中是常量)
    实现更多基元(目前仅支持三角形)

    [Edit1]代码调试和升级

    我删除了旧的源代码以适合30KB的限制。如果需要,请从编辑历史记录中进行挖掘。为此花了一些时间进行更高级的调试,结果如下:



    这个版本解决了一些几何,准确性,领域问题和错误。我实现了反射和折射,如下图测试射线的调试图所示:



    在调试 View 中,只有多维数据集是透明的,而没有击中任何物体的最后一缕射线将被忽略。因此,如您所见,射线 split ...射线由于总反射角而在立方体内部终止。出于速度原因,我禁用了对象内部的所有反射。

    用于相交检测的32位floats的距离有点嘈杂,因此您可以改用64位doubles,但是在这种情况下速度会大大降低。另一种选择是重写方程式以使用相对坐标,在这种情况下使用相对坐标更为精确。

    这里是float着色器源:

    顶点:

    //------------------------------------------------------------------
    #version 420 core
    //------------------------------------------------------------------
    uniform float aspect;
    uniform float focal_length;
    uniform mat4x4 tm_eye;
    layout(location=0) in vec2 pos;
    
    out smooth vec2 txt_pos;    // frag position on screen <-1,+1> for debug prints
    out smooth vec3 ray_pos;    // ray start position
    out smooth vec3 ray_dir;    // ray start direction
    //------------------------------------------------------------------
    void main(void)
        {
        vec4 p;
        txt_pos=pos;
        // perspective projection
        p=tm_eye*vec4(pos.x/aspect,pos.y,0.0,1.0);
        ray_pos=p.xyz;
        p-=tm_eye*vec4(0.0,0.0,-focal_length,1.0);
        ray_dir=normalize(p.xyz);
    
        gl_Position=vec4(pos,0.0,1.0);
        }
    //------------------------------------------------------------------
    

    片段:

    //------------------------------------------------------------------
    #version 420 core
    //------------------------------------------------------------------
    // Ray tracer ver: 1.000
    //------------------------------------------------------------------
    in smooth vec3      ray_pos;    // ray start position
    in smooth vec3      ray_dir;    // ray start direction
    uniform float       n0;         // refractive index of camera origin
    uniform int         fac_siz;    // square texture x,y resolution size
    uniform int         fac_num;    // number of valid floats in texture
    uniform sampler2D   fac_txr;    // scene mesh data texture
    out layout(location=0) vec4 frag_col;
    //---------------------------------------------------------------------------
    //#define _debug_print
    #define _reflect
    #define _refract
    //---------------------------------------------------------------------------
    #ifdef _debug_print
    in vec2 txt_pos;                // frag screen position <-1,+1>
    uniform sampler2D txr_font;     // ASCII 32x8 characters font texture unit
    uniform float txt_fxs,txt_fys;  // font/screen resolution ratio
    const int _txtsiz=64;           // text buffer size
    int txt[_txtsiz],txtsiz;        // text buffer and its actual size
    vec4 txt_col=vec4(0.0,0.0,0.0,1.0); // color interface for txt_print()
    bool _txt_col=false;            // is txt_col active?
    void txt_decimal(vec2 v);       // print vec3 into txt
    void txt_decimal(vec3 v);       // print vec3 into txt
    void txt_decimal(vec4 v);       // print vec3 into txt
    void txt_decimal(float x);      // print float x into txt
    void txt_decimal(int x);        // print int x into txt
    void txt_print(float x0,float y0);  // print txt at x0,y0 [chars]
    #endif
    //---------------------------------------------------------------------------
    void main(void)
        {
        const vec3  light_dir=normalize(vec3(0.1,0.1,1.0));
        const float light_iamb=0.1;                 // dot offset
        const float light_idir=0.5;                 // directional light amplitude
        const vec3 back_col=vec3(0.2,0.2,0.2);      // background color
    
        const float _zero=1e-6;     // to avoid intrsection with start point of ray
        const int _fac_triangles=0; // r,g,b, refl,refr,n, type, triangle count, { x0,y0,z0,x1,y1,z1,x2,y2,z2 }
        const int _fac_spheres  =1; // r,g,b, refl,refr,n, type, sphere count,   { x,y,z,r }
        // ray scene intersection
        struct _ray
            {
            vec3 pos,dir,nor;
            vec3 col;
            float refl,refr;// reflection,refraction intensity coeficients
            float n0,n1,l;  // refaction index (start,end) , ray length
            int lvl,i0,i1;  // recursion level, reflect, refract
            };
        const int _lvls=5;
        const int _rays=(1<<_lvls)-1;
        _ray ray[_rays]; int rays;
    
        vec3 v0,v1,v2,pos;
        vec3 c,col;
        float refr,refl;
        float tt,t,n1,a;
        int i0,ii,num,id;
    
        // fac texture access
        vec2 st; int i,j; float ds=1.0/float(fac_siz-1);
        #define fac_get texture(fac_txr,st).r; st.s+=ds; i++; j++; if (j==fac_siz) { j=0; st.s=0.0; st.t+=ds; }
        // enque start ray
        ray[0].pos=ray_pos;
        ray[0].dir=normalize(ray_dir);
        ray[0].nor=vec3(0.0,0.0,0.0);
        ray[0].refl=0.0;
        ray[0].refr=0.0;
        ray[0].n0=n0;
        ray[0].n1=1.0;
        ray[0].l =0.0;
        ray[0].lvl=0;
        ray[0].i0=-1;
        ray[0].i1=-1;
        rays=1;
    
        // debug print area
        #ifdef _debug_print
        bool _dbg=false;
        float dbg_x0=45.0;
        float dbg_y0= 1.0;
        float dbg_xs=12.0;
        float dbg_ys=_rays+1.0;
    
        dbg_xs=40.0;
        dbg_ys=10;
    
        float x=0.5*(1.0+txt_pos.x)/txt_fxs; x-=dbg_x0;
        float y=0.5*(1.0-txt_pos.y)/txt_fys; y-=dbg_y0;
        // inside bbox?
        if ((x>=0.0)&&(x<=dbg_xs)
          &&(y>=0.0)&&(y<=dbg_ys))
            {
            // prints on
            _dbg=true;
            // preset debug ray
            ray[0].pos=vec3(0.0,0.0,0.0)*2.5;
            ray[0].dir=vec3(0.0,0.0,1.0);
            }
        #endif
    
        // loop all enqued rays
        for (i0=0;i0<rays;i0++)
            {
            // loop through all objects
            // find closest forward intersection between them and ray[i0]
            // strore it to ray[i0].(nor,col)
            // strore it to pos,n1
            t=tt=-1.0; ii=1; ray[i0].l=0.0;
            ray[i0].col=back_col;
            pos=ray[i0].pos; n1=n0;
            for (st=vec2(0.0,0.0),i=j=0;i<fac_num;)
                {
                c.r=fac_get;            // RGBA
                c.g=fac_get;
                c.b=fac_get;
                refl=fac_get;
                refr=fac_get;
                n1=fac_get;             // refraction index
                a=fac_get; id=int(a);   // object type
                a=fac_get; num=int(a);  // face count
    
                if (id==_fac_triangles)
                 for (;num>0;num--)
                    {
                    v0.x=fac_get; v0.y=fac_get; v0.z=fac_get;
                    v1.x=fac_get; v1.y=fac_get; v1.z=fac_get;
                    v2.x=fac_get; v2.y=fac_get; v2.z=fac_get;
                    vec3 e1,e2,n,p,q,r;
                    float t,u,v,det,idet;
                    //compute ray triangle intersection
                    e1=v1-v0;
                    e2=v2-v0;
                    // Calculate planes normal vector
                    p=cross(ray[i0].dir,e2);
                    det=dot(e1,p);
                    // Ray is parallel to plane
                    if (abs(det)<1e-8) continue;
                    idet=1.0/det;
                    r=ray[i0].pos-v0;
                    u=dot(r,p)*idet;
                    if ((u<0.0)||(u>1.0)) continue;
                    q=cross(r,e1);
                    v=dot(ray[i0].dir,q)*idet;
                    if ((v<0.0)||(u+v>1.0)) continue;
                    t=dot(e2,q)*idet;
                    if ((t>_zero)&&((t<=tt)||(ii!=0)))
                        {
                        ii=0; tt=t;
                        // store color,n ...
                        ray[i0].col=c;
                        ray[i0].refl=refl;
                        ray[i0].refr=refr;
                        // barycentric interpolate position
                        t=1.0-u-v;
                        pos=(v0*t)+(v1*u)+(v2*v);
                        // compute normal (store as dir for now)
                        e1=v1-v0;
                        e2=v2-v1;
                        ray[i0].nor=cross(e1,e2);
                        }
                    }
    
                if (id==_fac_spheres)
                 for (;num>0;num--)
                    {
                    float r;
                    v0.x=fac_get; v0.y=fac_get; v0.z=fac_get; r=fac_get;
                    // compute l0 length of ray(p0,dp) to intersection with sphere(v0,r)
                    // where rr= r^-2
                    float aa,bb,cc,dd,l0,l1,rr;
                    vec3 p0,dp;
                    p0=ray[i0].pos-v0;  // set sphere center to (0,0,0)
                    dp=ray[i0].dir;
                    rr = 1.0/(r*r);
                    aa=2.0*rr*dot(dp,dp);
                    bb=2.0*rr*dot(p0,dp);
                    cc=    rr*dot(p0,p0)-1.0;
                    dd=((bb*bb)-(2.0*aa*cc));
                    if (dd<0.0) continue;
                    dd=sqrt(dd);
                    l0=(-bb+dd)/aa;
                    l1=(-bb-dd)/aa;
                    if (l0<0.0) l0=l1;
                    if (l1<0.0) l1=l0;
                    t=min(l0,l1); if (t<=_zero) t=max(l0,l1);
                    if ((t>_zero)&&((t<=tt)||(ii!=0)))
                        {
                        ii=0; tt=t;
                        // store color,n ...
                        ray[i0].col=c;
                        ray[i0].refl=refl;
                        ray[i0].refr=refr;
                        // position,normal
                        pos=ray[i0].pos+(ray[i0].dir*t);
                        ray[i0].nor=pos-v0;
                        }
                    }
                }
            ray[i0].l=tt;
            ray[i0].nor=normalize(ray[i0].nor);
            // split ray from pos and ray[i0].nor
            if ((ii==0)&&(ray[i0].lvl<_lvls-1))
                {
                t=dot(ray[i0].dir,ray[i0].nor);
    
                // reflect
                #ifdef _reflect
                if ((ray[i0].refl>_zero)&&(t<_zero))    // do not reflect inside objects
                    {
                    ray[i0].i0=rays;
                    ray[rays]=ray[i0];
                    ray[rays].lvl++;
                    ray[rays].i0=-1;
                    ray[rays].i1=-1;
                    ray[rays].pos=pos;
                    ray[rays].dir=ray[rays].dir-(2.0*t*ray[rays].nor);
                    ray[rays].n0=ray[i0].n0;
                    ray[rays].n1=ray[i0].n0;
                    rays++;
                    }
                #endif
    
                // refract
                #ifdef _refract
                if (ray[i0].refr>_zero)
                    {
                    ray[i0].i1=rays;
                    ray[rays]=ray[i0];
                    ray[rays].lvl++;
                    ray[rays].i0=-1;
                    ray[rays].i1=-1;
                    ray[rays].pos=pos;
    
                    t=dot(ray[i0].dir,ray[i0].nor);
                    if (t>0.0)  // exit object
                        {
                        ray[rays].n0=ray[i0].n0;
                        ray[rays].n1=n0;
                        v0=-ray[i0].nor; t=-t;
                        }
                    else{       // enter object
                        ray[rays].n0=n1;
                        ray[rays].n1=ray[i0].n0;
                        ray[i0  ].n1=n1;
                        v0=ray[i0].nor;
                        }
                    n1=ray[i0].n0/ray[i0].n1;
                    tt=1.0-(n1*n1*(1.0-t*t));
                    if (tt>=0.0)
                        {
                        ray[rays].dir=(ray[i0].dir*n1)-(v0*((n1*t)+sqrt(tt)));
                        rays++;
                        }
                    }
                #endif
                }
            else if (i0>0) // ignore last ray if nothing hit
                {
                ray[i0]=ray[rays-1];
                rays--; i0--;
                }
            }
        // back track ray intersections and compute output color col
        // lvl is sorted ascending so backtrack from end
        for (i0=rays-1;i0>=0;i0--)
            {
            // directional + ambient light
            t=abs(dot(ray[i0].nor,light_dir)*light_idir)+light_iamb;
            t*=1.0-ray[i0].refl-ray[i0].refr;
            ray[i0].col.rgb*=t;
            // reflect
            ii=ray[i0].i0;
            if (ii>=0) ray[i0].col.rgb+=ray[ii].col.rgb*ray[i0].refl;
            // refract
            ii=ray[i0].i1;
            if (ii>=0) ray[i0].col.rgb+=ray[ii].col.rgb*ray[i0].refr;
            }
    
        col=ray[0].col;
    
        // debug prints
        #ifdef _debug_print
    /*
        if (_dbg)
            {
            txtsiz=0;
            txt_decimal(_lvls);
            txt[txtsiz]=' '; txtsiz++;
            txt_decimal(rays);
            txt[txtsiz]=' '; txtsiz++;
            txt_decimal(_rays);
            txt_print(dbg_x0,dbg_y0);
    
            for (ii=0;ii<rays;ii++)
                {
                txtsiz=0;
                txt_decimal(ray[ii].lvl);
                txt_print(dbg_x0,dbg_y0+ii+1);
                }
    
            for (ii=0,st=vec2(0.0,0.0),i=j=0;i<fac_num;ii++)
                {
                c.r=fac_get;            // RGBA
                txtsiz=0;
                txt_decimal(c.r);
                txt_print(dbg_x0,dbg_y0+ii+1);
                }
            if (_txt_col) col=txt_col.rgb;
            }
    */
        if (_dbg)
            {
            float x=dbg_x0,y=dbg_y0;
            vec3 a=vec3(1.0,2.0,3.0);
            vec3 b=vec3(5.0,6.0,7.0);
            txtsiz=0; txt_decimal(dot(a,b)); txt_print(x,y); y++;
            txtsiz=0; txt_decimal(cross(a,b)); txt_print(x,y); y++;
            if (_txt_col) col=txt_col.rgb;
            }
        #endif
    
        frag_col=vec4(col,1.0);
        }
    //---------------------------------------------------------------------------
    #ifdef _debug_print
    //---------------------------------------------------------------------------
    void txt_decimal(vec2 v)        // print vec2 into txt
        {
                          txt[txtsiz]='('; txtsiz++;
        txt_decimal(v.x); txt[txtsiz]=','; txtsiz++;
        txt_decimal(v.y); txt[txtsiz]=')'; txtsiz++;
        txt[txtsiz]=0;  // string terminator
        }
    //---------------------------------------------------------------------------
    void txt_decimal(vec3 v)        // print vec3 into txt
        {
                          txt[txtsiz]='('; txtsiz++;
        txt_decimal(v.x); txt[txtsiz]=','; txtsiz++;
        txt_decimal(v.y); txt[txtsiz]=','; txtsiz++;
        txt_decimal(v.z); txt[txtsiz]=')'; txtsiz++;
        txt[txtsiz]=0;  // string terminator
        }
    //---------------------------------------------------------------------------
    void txt_decimal(vec4 v)        // print vec4 into txt
        {
                          txt[txtsiz]='('; txtsiz++;
        txt_decimal(v.x); txt[txtsiz]=','; txtsiz++;
        txt_decimal(v.y); txt[txtsiz]=','; txtsiz++;
        txt_decimal(v.z); txt[txtsiz]=','; txtsiz++;
        txt_decimal(v.w); txt[txtsiz]=')'; txtsiz++;
        txt[txtsiz]=0;  // string terminator
        }
    //---------------------------------------------------------------------------
    void txt_decimal(float x)       // print float x into txt
        {
        int i,j,c;                  // l is size of string
        float y,a;
        const float base=10;
        // handle sign
        if (x<0.0) { txt[txtsiz]='-'; txtsiz++; x=-x; }
         else      { txt[txtsiz]='+'; txtsiz++; }
        // divide to int(x).fract(y) parts of number
        y=x; x=floor(x); y-=x;
        // handle integer part
        i=txtsiz;                   // start of integer part
        for (;txtsiz<_txtsiz;)
            {
            a=x;
            x=floor(x/base);
            a-=base*x;
            txt[txtsiz]=int(a)+'0'; txtsiz++;
            if (x<=0.0) break;
            }
        j=txtsiz-1;                 // end of integer part
        for (;i<j;i++,j--)          // reverse integer digits
            {
            c=txt[i]; txt[i]=txt[j]; txt[j]=c;
            }
        // handle fractional part
        for (txt[txtsiz]='.',txtsiz++;txtsiz<_txtsiz;)
            {
            y*=base;
            a=floor(y);
            y-=a;
            txt[txtsiz]=int(a)+'0'; txtsiz++;
            if (y<=0.0) break;
            }
        txt[txtsiz]=0;  // string terminator
        }
    //---------------------------------------------------------------------------
    void txt_decimal(int x)     // print int x into txt
        {
        int a,i,j,c;            // l is size of string
        const int base=10;
        // handle sign
        if (x<0.0) { txt[txtsiz]='-'; txtsiz++; x=-x; }
         else      { txt[txtsiz]='+'; txtsiz++; }
        // handle integer part
        i=txtsiz;               // start of integer part
        for (;txtsiz<_txtsiz;)
            {
            a=x;
            x/=base;
            a-=base*x;
            txt[txtsiz]=int(a)+'0'; txtsiz++;
            if (x<=0) break;
            }
        j=txtsiz-1;             // end of integer part
        for (;i<j;i++,j--)      // reverse integer digits
            {
            c=txt[i]; txt[i]=txt[j]; txt[j]=c;
            }
        txt[txtsiz]=0;  // string terminator
        }
    //---------------------------------------------------------------------------
    void txt_print(float x0,float y0)   // print txt at x0,y0 [chars]
        {
        int i;
        float x,y;
        // fragment position [chars] relative to x0,y0
        x=0.5*(1.0+txt_pos.x)/txt_fxs; x-=x0;
        y=0.5*(1.0-txt_pos.y)/txt_fys; y-=y0;
        // inside bbox?
        if ((x<0.0)||(x>float(txtsiz))||(y<0.0)||(y>1.0)) return;
        // get font texture position for target ASCII
        i=int(x);               // char index in txt
        x-=float(i);
        i=txt[i];
        x+=float(int(i&31));
        y+=float(int(i>>5));
        x/=32.0; y/=8.0;    // offset in char texture
        txt_col=texture(txr_font,vec2(x,y));
        _txt_col=true;
        }
    //---------------------------------------------------------------------------
    #endif
    //---------------------------------------------------------------------------
    

    代码尚未优化,但我想先使物理正确工作。仍然没有实现菲涅耳,但是使用了 Material 的refl,refr系数来代替。

    您也可以忽略调试打印内容(它们被#define封装)。

    我为几何纹理建立了一个小类,以便可以轻松设置场景对象。这是为预览启动场景的方式:

    ray.beg();
    //                 r   g   b rfl rfr   n
    ray.add_material(1.0,1.0,1.0,0.3,0.0,_n_glass); ray.add_box   ( 0.0, 0.0, 6.0,9.0,9.0,0.1);
    ray.add_material(1.0,1.0,1.0,0.1,0.8,_n_glass); ray.add_sphere( 0.0, 0.0, 0.5,0.5);
    ray.add_material(1.0,0.1,0.1,0.3,0.0,_n_glass); ray.add_sphere( +2.0, 0.0, 2.0,0.5);
    ray.add_material(0.1,1.0,0.1,0.3,0.0,_n_glass); ray.add_box   ( -2.0, 0.0, 2.0,0.5,0.5,0.5);
    ray.add_material(0.1,0.1,1.0,0.3,0.0,_n_glass);
    ray.add_tetrahedron
        (
         0.0, 0.0, 3.0,
        -1.0,-1.0, 4.0,
        +1.0,-1.0, 4.0,
         0.0,+1.0, 4.0
        );
    ray.end();
    

    重要的是,要使计算的法线面向对象之外,因为该法线用于检测内部/外部对象的交叉。

    P.S.

    如果您对此感兴趣,请参阅我的3D体积背射线示踪剂:
  • How to best write a voxel engine in C with performance in mind

  • 这里是支持半球对象的“Mesh”射线追踪器的较新版本:
  • Ray tracing a Hemisphere
  • 关于c++ - 如果没有递归光线跟踪,反射和折射是不可能的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42876586/

    有关c++ - 如果没有递归光线跟踪,反射和折射是不可能的吗?的更多相关文章

    1. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

      我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

    2. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

      我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

    3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

      我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

    4. ruby-on-rails - rails 目前在重启后没有安装 - 2

      我有一个奇怪的问题:我在rvm上安装了ruby​​onrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(

    5. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    6. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

      我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

    7. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

      如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

    8. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

      我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

    9. ruby-on-rails - 如果我将 ruby​​ 版本 2.5.1 与 rails 版本 2.3.18 一起使用会怎样? - 2

      如果我使用ruby​​版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby​​1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更

    10. 没有类的 Ruby 方法? - 2

      大家好!我想知道Ruby中未使用语法ClassName.method_name调用的方法是如何工作的。我头脑中的一些是puts、print、gets、chomp。可以在不使用点运算符的情况下调用这些方法。为什么是这样?他们来自哪里?我怎样才能看到这些方法的完整列表? 最佳答案 Kernel中的所有方法都可用于Object类的所有对象或从Object派生的任何类。您可以使用Kernel.instance_methods列出它们。 关于没有类的Ruby方法?,我们在StackOverflow

    随机推荐