Spherical Harmonics Lighting代码实现(续)

上一篇文章我们已经可以求出由SH基函数组成特定复合函数的因子,现在我们就来实现三维空间的SHL,光源来自HDR图像。

该算法使用上一篇文章的SH因子计算过程、HDR图像导入过程,同时也使用了光线/物体相交过程。

这里新引进几个结构体。

SHRay-使用两个SHVector3d表示射线的起点和方向。

SHRGBColor-表示RGB颜色值,每个分量都是double精度的浮点数。

SHCoeff-为每一个颜色通道容纳SH因子。

SHMaterial-包含物体表面属性:ambient,diffuse,specular,specular power,最后一个分量作为物体的uid。

SHMaterialLookup-一个查找表。因为3DS文件使用名字表示材质,而不是一个uid。这个结构体有两个成员:一个string代表材质名称,一个整数表示和SHMaterial结构体中uid相对应的uid。这个uid作为材质数组的索引。

SHFace3d-容纳场景中给定三角形的特征。成员有:附着在三角形上的材质uid,一个代表法线的向量,含有三个索引的数组(这三个索引分别指向三个顶点),常数(用来搞笑检测交点),两个主轴索引(在求交点过程中三角形投影平面的主轴)。

SHMesh3d-场景中物体的特征,成员有:顶点个数,三角面片个数,两个向量代表包围盒的范围(用来加速求交),三角形数组,顶点数组,每个顶点的法线数组,每一个转移方程的SH因子(不考虑自阴影、自阴影、全局光照)。

SHCamera3d-包含摄像机的属性,成员有:位置(SHVector3d),目标向量(SHVector3d),视野范围(FOV)。

SHScene3d-包含3D场景中的一切东西:摄像机的数目,光源数目,材质数目,物体数目,三角面片总数,场景物体数组,默认材质,材质查找表,背景色,摄像机数组,光源数组。

QQ截图20120531201731

 

1、SH Diffuse Unshadowed Light

201205291620328425

/*
point:要计算SH因子的当前顶点
normal:当前顶点的法线
color:当前顶点的颜色
sphericalSamples:在所有SH计算中都要使用的样本
result:结果因子
numSamples:样本数
numCoeffs:因子数
*/

void SHProjectDiffuseUnshadowedVertex(SHVector3d point,SHVector3d normal,SHRGBColor color,SHSample* sphericalSamples,SHRGBColor* result,int numSamples,int numCoeffs)
{
     //反射率
     SHRGBColor albedo;
 
     //dot product and scale coefficient
     double H,value;
     
    //weighting factor
    double dWeight=4.0*PI;
    double factor=dWeight/numSamples;

    //indexes
    int i,n;

    //loop through all the spherical samples
    for(i=0;i<numSamples;++i)
    {
         //the transfer function is the cosine item
         VecDot(sphericalSamples[i].vec,normal,H);

         //calculate the albedo
         RGBScale(color,ONE_OVER_PI,albedo);
  
         //calculate only if cosine positive
         if(H>0.0)
         {
            //calculate the coefficient
            for(n=0;n<numCoeffs;++n)
            {
                 value=H*sphericalSamples[i].coeff[n];
                 result[n].r+=albedo.r*value;
                 result[n].g+=albedo.g*value;
                 result[n].b+=albedo.b*value;
            }
         }
    }
    //scale the SH coefficient
    for(i=0;i<numCoeffs;++i)
    {
         result[i].r*=factor;
         result[i].g*=factor;
         result[i].b*=factor;
    }
}

以上代码是求给定点转移方程的SH投影(即求系数),是预处理的一步。


2、SH Diffuse Shadowed Light

201205291620347670

转移方程包含可见项,需要检测和场景中所有其他三角面片的相交情况,需要使用光线追踪的算法。对于一个给定的顶点,我们不去检测它和场景中所有物体的所有三角面片的相交情况,而是我们首先检测它和每个物体包围盒的相交情况,如果相交,则继续检测它和该物体所有三角面片的相交情况。

/*
aScene:被照亮的场景
meshIndex:当前顶点属于的mesh
faceIndex:当前顶点属于的三角面片
*/
void SHProjectDiffuseShadowedVertex(SHScene3d* aScene,int meshIndex,int faceIndex,SHVector3d point,SHVector3d normal,SHRGBColor color,SHSample* sphericalSamples,SHRGBColor* result,int numSample,int numCoeffs)
{
   //反射率
   SHRGBColor albedo;
   
   //dot product and scale coefficient
   double H,value;    

   //weighting factor
   double dWeight=4.0*PI;
   double factor=dWeight/numSamples;
   
   //indexes
   int i,n;

   //ray used for the visibility term
   SHRay ray;
   
   //origin of the ray is the current vertex
   VecCopy(point,ray.origin);

   //loop through all the spherical samples
   for(i=0;i<numSamples;++i)
   {
       VecDot(sphericalSamples[i].vec,normal,H);
       
       RGBScale(color,ONE_OVER_PI,albedo);

       if(H>0.0)
       {
            //the direction is the spherical sample direction
            VecCopy(sphericalSamples[i].vec,ray.direction);
   
            //determine the visibility for shadowing
            if(!intersectScene(&ray,aScene,faceIndex,meshIndex))
            {
                for(n=0;n<numCoeffs;++n)
                {
                    value=H*sphericalSamples[i].coeff[n];
                    result[n]+=albedo*value;
                }
            }
       }
   }
   for(n=0;n<numCoeffs;++n)
      result[n]*=factor;   
}

int intersectScene(SHRay* ray,SHScene3d* aScene,int faceIndex,int meshIndex)
{
   //the current mesh
   SHMesh3d* mesh;
   
   //indexes
   int i,j;

   //go through each object of the model
   for(i=0;i<aScene->numMeshes;++i)
   {
       //assign current mesh
       mesh=&aScene->meshes[i];
    
      //check if ray intersects the mesh's bounding box
      if(!boxIntersect(ray,mesh->min,mesh->max))
         continue;
   
      //go through all of the faces of the object
      for(j=0;j<mesh->numFaces;++j)
      {
          //skip triangles from which the ray orginated
          if(i==meshIndex && j==faceIndex)
             continue;
 
          //test intersection,returns if intersection
          if(triangleIntersect(&mesh->faces[j],mesh->points[mesh->faces[j].pointIndex[0]],mesh->points[mesh->faces[j].pointIndex[1]],
mesh->points[mesh->faces[j].pointIndex[2]].ray))
          return 1;  
      }
   }
   
   //returns 0 if no intersection found
   return 0;
}

下面的程序实时计算每一个顶点的最终颜色值,用到SH基函数的性质2

201205291620188340

SHRGBColor SHLighting(SHRGBColor* light,SHRGBColor* vertexSH,
int numCoeffs,double dScale)
{
    .//the color returned
     SHRGBColor result;

    //index
    int i;

    //initialize the color
    result.r=result.g=result.b=0.0;

    //perform the dot product of the SH coefficients
    for(i=0;i<numCoeffs;++i)
    {
        result+=light[i]*vertexSH[i]*dScale;
    }
    
    return result;
}
posted @ 2012-05-31 22:51  Cavia  阅读(888)  评论(1编辑  收藏  举报