【译】光线跟踪:理论与实现(三) 折射与Lambert-Beer 定律

简介:
Introduction
      作者在这一篇中将解释如何去跟踪折射光线。这将涉及到在相交点处产生新的光线,并且计算新光线的方向。此外,作者还将运用Lambert-Beer 定律来解释光线在物体内部的吸收情况。最后作者将展示如何加入反锯齿的效果,并且如何对光线跟踪器进行加速优化。
Refractions
折射的情况如图1所示,注意观察光线在几何体表面是如何改变方向的,又是如何穿过几何体后面那个点的。在这个点后面的物体会看起来是倒转的镜像。
      几何体表面处光线的弯曲程度取决于两种物质的折射率:光线进入几何体前所在的物质的折射率,构成几何体的物质的折射率。例如:空气和真空的折射率为1.0,20摄氏度的水的折射率为1.33。
   2008041302.jpg
   下面这段代码应该让你感到很熟悉了吧。它就是构造折射光线,递归地跟踪它,并将结果颜色值加入到产生折射光的光线中去。有一点值得注意:法向量乘以了一个值’result’。这个值是对每个几何体的相交测试时获得的,值是1或0,分别表示是否命中。当然,还有第3个选择:-1,这表示是从几何体内部命中的。这点也是很重要的:如果一个光线命是从外部命中一个几何体的,那其实是没有命中这个几何体,而是它附件的物质。因此,其法向量应该取反。
    // calculate refraction
        float refr = prim->GetMaterial()->GetRefraction();
        if ((refr > 0) && (a_Depth < TRACEDEPTH))
        {
            float rindex = prim->GetMaterial()->GetRefrIndex();
            float n = a_RIndex / rindex;
            vector3 N = prim->GetNormal( pi ) * (float)result;
            float cosI = -DOT( N, a_Ray.GetDirection() );
            float cosT2 = 1.0f - n * n * (1.0f - cosI * cosI);
            if (cosT2 > 0.0f)
            {
                vector3 T = (n * a_Ray.GetDirection()) + (n * cosI - sqrtf( cosT2 )) * N;
                Color rcol( 0, 0, 0 );
                float dist;
                Raytrace( Ray( pi + T * EPSILON, T ), rcol, a_Depth + 1, rindex, dist );
                a_Acc += rcol * transparency;
            }
        }


下图就是加入折射的效果图:
   2008041303.jpg
毫无疑问,你会注意到现在光线跟踪器很慢的,这是因为每条光线要与每个几何体进行相交测试来找出最近的相交点。当然,有很多方法可以对其进行改进,作者在后面的文章中会使用一种空间细分的方法来对相交测试的数量进行限制的。
Lambert-Beer定律
      上面的图中,你会发现球是蓝色的,因此折射图的颜色也是稍微偏蓝色的。这是因为折射光返回的颜色值会与几何体颜色相乘。这在反射,散射以及镜面光的计算中都有这种情况。
      现在让我们来想象有这么一个水池,里面充满了带颜色的物质(比如说水里面混合着蓝墨水)。池子的浅处大概10cm深,深处呢有1米深。如果你从上往底下看,很明显可以看到在较深的那端的底部受到颜色的影响会比浅处的要大。这种效应就叫Beer定律。
      Beer定律可以用下列公式表示:
light_out = light_in * e(e * c * d)
这个公式主要是用来计算溶解在水中的物质的光吸收度的。’e’是一个常量,表明溶剂的吸收度(准确来说,是单位为L/mol*cm的摩尔吸光率)。’c’是溶质的数量,单位mol/L.’d’是光线的路径长度。一般我们可以简化公式如下:

light_out = light_in * e(d * C)
这个d是路径长度,C是一个常量,表示物质的密度,减小它的值会使得光线在穿越物体时的时间增大。
吸收及透明度的计算需要以颜色因子来单位进行逐个计算。
                // apply Beer's law
                Color absorbance = prim->GetMaterial()->GetColor() * 0.15f * -dist;
                Color transparency = Color( expf( absorbance.r ), expf( absorbance.g ), expf( absorbance.b ) );
                a_Acc += rcol * transparency;

下面是效果图:
2008041301.jpg
   对于目前程序中使用的场景来说,应用这个定律对于画面的质量没有多大影响。不过一旦你开始使用复杂的场景,情况就大不一样了。
很多光线跟踪器都使用下述简单的方法:每个物体都有一个“反射”变量,它会与折射光返回的颜色值相乘,同时还有一个“折射”变量,会与折射光返回的颜色值相乘。然而对于折射光来说,这并不正确:每条折射光线在进入和退出几何体时会被计算两次。而且,从一个薄的物体穿过与穿过一个厚的物体有相同的衰减度。最大的问题更在于我们直观上会感觉不对:光线密度在几何体表面没有减小,而只会在几何体内部减小。
SuperSampling
假设我们使用下列代码来替换产生光线处的代码:
for ( int tx = 0; tx < 4; tx++ ) for ( int ty = 0; ty < 4; ty++ )
{
    vector3 dir = vector3( m_SX + m_DX * tx / 4.0f, m_SY + m_DY * ty / 4.0f, 0 ) - o;
    NORMALIZE( dir );
    Ray r( o, dir );
    float dist;
    Primitive* prim = Raytrace( r, acc, 1, 1.0f, dist );
}

int red = (int)(acc.r * 16);
int green = (int)(acc.g * 16);
int blue = (int)(acc.b * 16); 

这段代码作用是为每个像素点发射16条光线,因此结果图像具有抗锯齿效果,但缺点就是耗时太多。
也许你注意到了光线跟踪器返回的是指向几何体的指针,它指向被primary ray命中的几何体。我们做如下修改,代码的性能就大幅提升:
// fire primary rays
Color acc( 0, 0, 0 );
vector3 dir = vector3( m_SX, m_SY, 0 ) - o;
NORMALIZE( dir );
Ray r( o, dir );
float dist;
Primitive* prim = Raytrace( r, acc, 1, 1.0f, dist );
int red, green, blue;
if (prim != lastprim)
{
        lastprim = prim;
    Color acc( 0, 0, 0 );
    for ( int tx = -1; tx < 2; tx++ ) for ( int ty = -1; ty < 2; ty++ )
    {
        vector3 dir = vector3( m_SX + m_DX * tx / 2.0f, m_SY + m_DY * ty / 2.0f, 0 ) - o;
        NORMALIZE( dir );
        Ray r( o, dir );
        float dist;
        Primitive* prim = Raytrace( r, acc, 1, 1.0f, dist );
    }
    red = (int)(acc.r * (256 / 9));
    green = (int)(acc.g * (256 / 9));
    blue = (int)(acc.b * (256 / 9));
}

else
{
    red = (int)(acc.r * 256);
    green = (int)(acc.g * 256);
    blue = (int)(acc.b * 256);
}
if (red > 255) red = 255;
if (green > 255) green = 255;

if (blue > 255) blue = 255;

ok,it’s over.在下一篇中作者将介绍“空间分割”思想的运用。

原文链接:http://www.devmaster.net/articles/raytracing_series/part3.php




本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2008/04/15/1155020.html,如需转载请自行联系原作者
目录
相关文章
|
8月前
混沌引力搜索算法(CGSA)解决三个机械工程设计问题(Matlab代码实现)
混沌引力搜索算法(CGSA)解决三个机械工程设计问题(Matlab代码实现)
|
9月前
|
算法 定位技术
基于改进的混沌引力常数的引力搜索算法(Matlab代码实现)
基于改进的混沌引力常数的引力搜索算法(Matlab代码实现)
|
11月前
|
Go 量子技术
用量子理论凭空生成能量:物理学家学会使用零点能
用量子理论凭空生成能量:物理学家学会使用零点能
|
机器学习/深度学习 移动开发 算法
【物理应用】基于摩尔库仑准则实现应变软化岩体的围岩-支护相互作用附matlab代码
【物理应用】基于摩尔库仑准则实现应变软化岩体的围岩-支护相互作用附matlab代码
【物理应用】基于摩尔库仑准则实现应变软化岩体的围岩-支护相互作用附matlab代码
|
存储 人工智能 算法
这些计算机领域的重要定律你都清楚吗?
这些计算机领域的重要定律你都清楚吗?
307 0
这些计算机领域的重要定律你都清楚吗?
|
机器学习/深度学习 算法 机器人
图文详解牛顿迭代法,牛顿不止力学三定律
图文详解牛顿迭代法,牛顿不止力学三定律
256 0
图文详解牛顿迭代法,牛顿不止力学三定律
计算机科学三大定律和网络三大定律
计算机科学三大定律和网络三大定律,了解一下。
4154 0