目前专业做水果的网站有哪些,wordpress多重查询,wordpress内存缓存插件,郑州网站排名优化外包Phinecos(洞庭散人) 专注于开源技术的研究与应用 【译】光线跟踪#xff1a;理论与实现#xff08;三#xff09; 折射与Lambert-Beer 定律 Introduction 作者在这一篇中将解释如何去跟踪折射光线。这将涉及到在相交点处产生新的光线#xff0c;并且计算新光线的方向。此外… Phinecos(洞庭散人) 专注于开源技术的研究与应用 【译】光线跟踪理论与实现三 折射与Lambert-Beer 定律 Introduction 作者在这一篇中将解释如何去跟踪折射光线。这将涉及到在相交点处产生新的光线并且计算新光线的方向。此外作者还将运用Lambert-Beer 定律来解释光线在物体内部的吸收情况。最后作者将展示如何加入反锯齿的效果并且如何对光线跟踪器进行加速优化。 Refractions 折射的情况如图1所示,注意观察光线在几何体表面是如何改变方向的又是如何穿过几何体后面那个点的。在这个点后面的物体会看起来是倒转的镜像。 几何体表面处光线的弯曲程度取决于两种物质的折射率光线进入几何体前所在的物质的折射率构成几何体的物质的折射率。例如空气和真空的折射率为1.020摄氏度的水的折射率为1.33。 下面这段代码应该让你感到很熟悉了吧。它就是构造折射光线递归地跟踪它并将结果颜色值加入到产生折射光的光线中去。有一点值得注意法向量乘以了一个值’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; } } 下图就是加入折射的效果图 毫无疑问你会注意到现在光线跟踪器很慢的这是因为每条光线要与每个几何体进行相交测试来找出最近的相交点。当然有很多方法可以对其进行改进作者在后面的文章中会使用一种空间细分的方法来对相交测试的数量进行限制的。 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 Beers 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; 下面是效果图 对于目前程序中使用的场景来说应用这个定律对于画面的质量没有多大影响。不过一旦你开始使用复杂的场景情况就大不一样了。 很多光线跟踪器都使用下述简单的方法每个物体都有一个“反射”变量它会与折射光返回的颜色值相乘同时还有一个“折射”变量会与折射光返回的颜色值相乘。然而对于折射光来说这并不正确每条折射光线在进入和退出几何体时会被计算两次。而且从一个薄的物体穿过与穿过一个厚的物体有相同的衰减度。最大的问题更在于我们直观上会感觉不对光线密度在几何体表面没有减小而只会在几何体内部减小。 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 作者洞庭散人 出处http://phinecos.cnblogs.com/ 本博客遵从 Creative Commons Attribution 3.0 License若用于非商业目的您可以自由转载但请保留原作者信息和文章链接URL