renderman 的 NPR(非照片级)渲染(BMRT)

Renderman 的 NPR(非照片级)渲染 (BMRT)          by hmaya, Zhu Qin

近来,人们除了对照片级真实渲染的不断追求之外,还在3D渲染器中追求非照片级真实渲染(Non Photo-realistically Rendering),简称NPR,例如卡通渲染、水彩、铅笔等笔触的模拟。

Render Man对渲染的控制可谓自由、开放之极,尤其在完成最后的项目之后,我们更是不得不赞叹Render Man的三头六臂,样样都可以应付。

2002年第一次使用MAYA时,那时还是MAYA4.0。但是至今为止,我最喜欢的MAYA开机界面,还是MAYA4.0的,如图。

MAYA4.0的开机界面 这样我就一直在想,这幅画面是如何生成的。我的注意力放在了画面的条纹上。我在Photoshop里做过尝试,可以做一个单独的条纹的图层,然后图层混合模式设为叠加,可以大致得到类似的效果,如图果,如图。

 

Photoshop中叠加条纹  

   但是,问题来了,这样的效果虽然近似,但也很死板,例如,如果在原来画面的非常亮的部分,不想叠加条纹;或者反过来,我们想在暗部显示条纹,亮部不显示条纹,在Photoshop中就比较难实现了。

总结一下,问题就是,能否只让某一段亮度范围的图像与条纹叠加,而该亮度以外的图像不与条纹叠加?我们在Render Man中尝试了这个问题,并且很好的解决了,从而可以自由控制叠加条纹的亮度范围了。实现思路是这样的,首先在Render Man中制作重复的条纹。这个问题比较简单,把P切换到屏幕空间,然后使用mod()函数制作重复条纹,使用mix()函数叠加条纹。代码如下:

point Pscreen = transform("screen", P);/*将P转换到屏幕空间*/ 
float ss=xcomp(Pscreen);      
float tt=ycomp(Pscreen);
ss=mod(ss*frequency,1);/*重复后的每个单元的s*/ 
tt=mod(tt*frequency,1);/*重复后的每个单元的t*/ 
Ci=mix(Ci,linecolor,weight);

条纹制作好之后,就要解决如何控制混合范围的问题了。Mix()函数中有个混合权重,可以把混合权重与图像的亮度建立联系,即达到用亮度控制条纹的目的了。 

   获取图像亮度的一种快捷方法,是直接对RGB操作,将RGB三色通道转换为一个单通道的灰度值,作为亮度通道(Luminance)输出。具体的数学公式是:
Luminance = 0.3* Red + 0.59* Green + 0.11* Blue ;

最后结合Luminance范围修正混合权重即可。解决方案如下:

     float RR=comp(Ci,0);/*提取R分量*/ 
    float GG=comp(Ci,1);/*提取G分量*/ 
    float BB=comp(Ci,2);/*提取B分量*/ 
    float Luminance = 0.3* RR + 0.59* GG + 0.11* BB ; 
   float alpha=smoothstep(0,Luminance_min,Luminance) 
        
-smoothstep(Luminance_max,1,Luminance);/*控制叠加的亮度范围*/
   weight=alpha*weight;/*亮度与混合权重建立连接*/
  
Ci=mix(Ci,linecolor,weight);/* 混合条纹与图像*/ 

Render Man的测试结果如下:不控制叠加的亮度范围,条纹简单的叠加如图:

 

条纹简单的叠加  

控制叠加的亮度范围之后,叠加条纹的效果如图:

 控制叠加的亮度范围之后,叠加条纹 

当然,叠加和制作条纹时,我们使用了smoothstep()函数,来反锯齿,或者进行渐变过渡的叠加。对三维物体的渲染测如图:

 

 下面给出原代码line.sl


surface
line(
 float Ka        =  1;
        float Kd        = .5;
        float Ks        = .5;
        float fuzz      = 0.1;/*控制线框的模糊宽度*/
        float frequency = 80; /*控制线框的重复次数*/
        float width     = .1; /*控制线框宽度*/
        color linecolor =color (0.3,0.4,0.7);/*控制线的颜色*/
        float roughness = .1;
        float Luminance_max =0.8;/*亮度混合的最大阀值,0.8以上的值不混合*/
        float Luminance_min =0.4;/*亮度混合的最小阀值,0.4以下的值不混合*/
        string mapname = "green.tiff";
        color specularcolor =(1,1,1))
{
    normal Nf = faceforward (normalize(N),I);
    vector V = -normalize(I);
    color Ct;
  
    point Pscreen = transform("screen", P);/*将P转换到屏幕空间*/
   
/*setzcomp(Pscreen, 0);
    Pscreen = normalize(Pscreen);*/
   
     float ss=xcomp(Pscreen);
     float tt=ycomp(Pscreen);
     ss=mod(ss*frequency,1);/*重复后的每个单元的s*/
     tt=mod(tt*frequency,1);/*重复后的每个单元的t*/
   
    float weight; /*混合的权重*/
    if(tt<(0.5-fuzz-width)||tt>(0.5+fuzz+width))
       weight=0;
    else
   {
    float smooth(float e0,e1,e2,e3,x) {
                    return  smoothstep(e0,e1,x)-smoothstep(e2,e3,x);
                      }  /*e0到e1之间平滑过渡,e1到e2之间为1,e2到e3间平滑过渡 */
   
       weight=smooth(0.5-fuzz-width,0.5-width,0.5+width,0.5+width+fuzz,tt);
       weight=clamp(weight,0,1);
   };
 
if( mapname != "" )
  Ct = color texture(mapname,s/2,t/2);
              /* Use s and t texture( mapname,s,t)*/
 else
  Ct = Cs;

    Oi = Os;

    Ci = Oi * ( Ct * (Ka*ambient() + Kd*diffuse(Nf)) +
  specularcolor * Ks*specular(Nf,V,roughness));

    float RR=comp(Ci,0);/*提取R分量*/
    float GG=comp(Ci,1);/*提取G分量*/
    float BB=comp(Ci,2);/*提取B分量*/
   
    float Luminance = 0.3* RR + 0.59* GG + 0.11* BB ;
  
float alpha=smoothstep(0,Luminance_min,Luminance)-smoothstep(Luminance_max,1,Luminance);

weight=alpha*weight;
Ci=mix(Ci,linecolor,weight);

   /*——修改亮度的另外一种快捷方法,直接对RGB操作.—–*/
  /* 将RGB三色通道转换为一个单通道的灰度值,
   作为亮度通道(Luminance)输出。具体的数学公式是:
   Luminance = 0.3* Red + 0.59* Green + 0.11* Blue ;
    RR=0.30*a*RR;//a为亮度比例因子
    GG=0.59*a*GG;
    BB=0.11*a*BB;
  */
}

 

 

 

 

 

 

作者: hmaya

developer!

看完不过瘾?点此向作者提问

发表评论

邮箱地址不会被公开。 必填项已用*标注