CG 学习 (3)——片元光照(Fragment Lighting)

       摘要:用一个顶点光照的程序来分析Cg的程序如何写,说明上次封装的一个CGShader如何使用,并简单阐述Phong光照模型的原理。

        1.  顶点程序运行的效果:
         终于到了学编程最激动人心的时刻了,自己动手实现,我们要“以只看不练为耻,以勤于实践为荣”。第一个练习的效果用《The Cg Tutorial》中的第五章中的Fragment Lighting例子。程序中的模型用美丽女神Venus的雕像,渲染为青铜色,该模型是个3DS文件,网上读3DS文件类多如牛毛,随便下载一个导入就行。效果如下图:

         2. 简单的Phong光照模型
         这里的光照模型对经典的Phong光模型进行了修改和扩展,在这个模型里,将一个物体表面最终的颜色分解为以下几项:放射(Emissive)、环境反射(Ambient)、漫反射(Diffuse)和镜面反射(Specular),物体最终的光照效果由这几个分项光照效果叠加而来。
         放射项: Emissive = Ke                 (Ke代表模型材质的放射光颜色)
         环境反射项: Ambient = Ka X globalAmbient       (其中Ka代表材质的环境反射系数,globalAmbient表示灯光的环境光的颜色)
         漫反射项: Diffuse = Kd X lightColor X max( dot(N, L), 0 )        (其中 Kd为材质的漫反射系数,lightColor是灯光的漫反射光的颜色,N为物体表面的规范化的法向量,L为模型上的点指向灯光的规范化向量。max( dot(N, L), 0 ) 表示取N,L的点积值和零中较大的一个值。
         镜面反射项:Specular = Ks X lightColor X facing X (max(N, H), 0)shininess  (其中Ks 为材质的漫反射系数,lightColor同上,facing 取值当N, H的点积大于零在facing = 1, 否则facing = 0,max( dot(N, H)的意义同上,shininess 是其指数,H为L和V的中间向量的归一量,V是视点指向物体上一点的向量)
        总体光照效果: finalColor = Emissive + Ambient + Diffuse + Specular

        3.  CGShader的用法
        这个光照效果的实现用到了上次封装的CGShader类,这个练习用到了顶点Shader和片元Shader,其使用步骤如下代码所示:
         (1) 首先定义两个Shader对象:
                   CGShader   vertShader ( " vertShader " );                     //顶点Shader
                  CGShader   fragShader ( " fragShader " );                     //片元Shader
         (2) 初始化及获得两个Shader中的Uniform 变量:       

vertShader.createShader( true " vertlighting.cg " );
fragShader.createShader(
false " fraglighting.cg " );

void  AddCgParams()
{
    vertShader.addParam(
"modelViewProj");
    fragShader.addParam(
"globalAmbient");
    fragShader.addParam(
"lights[0].color");
    fragShader.addParam(
"lights[0].position");
    fragShader.addParam(
"lights[1].color");
    fragShader.addParam(
"lights[1].position");
    fragShader.addParam(
"eyePosition");
    fragShader.addParam(
"material.Ke");
    fragShader.addParam(
"material.Ka");
    fragShader.addParam(
"material.Ks");
    fragShader.addParam(
"material.Kd");
    fragShader.addParam(
"material.shininess");
}

      前面两行是从文件载入Cg程序并编译,后面的函数是获得两个Cg程序里面的Uniform变量的,vertShader比较简单,只有一个变量,光照效果用了两个灯光。
       (3) 接着给参数赋值:

void  SetParameters()
{
    fragShader.setParamArrayf(
"globalAmbient", globalAmbient);
    fragShader.setParamArrayf(
"lights[0].color", lightColor1);
    fragShader.setParamArrayf(
"lights[1].color", lightColor2);
    
float brassEmissive[3= {0.0f,  0.0f,  0.0f},
        brassAmbient[
3]  = {0.25f0.148f0.06475f},
        brassDiffuse[
3]  = {0.4f0.2368f0.1036f},
        brassSpecular[
3= {0.7746f0.4586f0.2006f},
        brassShininess 
= 76.8f;
    fragShader.setParamArrayf(
"material.Ke", brassEmissive);
    fragShader.setParamArrayf(
"material.Ka", brassAmbient);
    fragShader.setParamArrayf(
"material.Ks", brassSpecular);
    fragShader.setParamArrayf(
"material.Kd", brassDiffuse);
    fragShader.setParam1(
"material.shininess", brassShininess);
}

        (4)  在绘制函数里面启用Shader并绘制模型

                     Vector4f lightpos1  =  Vector4f( 100 * cos(lightAngle * DEG2RAD),  160 100 * sin(lightAngle * DEG2RAD), 1 );
    Vector4f lightpos2 
=  Vector4f( - 60 350 550 1 );
    Vector4f eyepos   
=  Vector4f( 0 , 0 , 400 , 1 );
    Vector4f lightPosition, eyePosition;

    glClear(GL_COLOR_BUFFER_BIT 
|  GL_DEPTH_BUFFER_BIT);     //  清除屏幕和深度缓存

                     
// draw model and send it to CG for lighting
    Matrix4f revModelMat, scaleMat, rotateMat, modelMat, viewMat, modleViewMat;
    viewMat.lookAt(eyepos.x,eyepos.y,eyepos.z, 
0 0 0 ,   0 1 0 );

    vertShader.activate();                                     
// 启用Shader
    fragShader.activate();
    modelMat.rotateX(xrot);
    rotateMat.rotateY(yrot);
    modelMat 
*=  rotateMat;                                    // 获得模型的模型变换矩阵
    revModelMat  =  modelMat; revModelMat.setInverse();
    lightPosition 
=  revModelMat  *  lightpos1;           // 把灯光的位置变换到物体局部空间
    eyePosition    =  revModelMat  *  eyepos;             // 把眼睛的位置变换到物体的局部空间
     float  lightp[ 3 =   {lightPosition.x, lightPosition.y, lightPosition.z} ;
    
float  eyep[ 3 ]    =   {eyePosition.x, eyePosition.y, eyePosition.z} ;
    fragShader.setParamArrayf(
" lights[0].position " , lightp);
    fragShader.setParamArrayf(
" eyePosition " , eyep);
    lightPosition 
=  revModelMat  *  lightpos2;
    
float  lightp2[ 3 =   {lightPosition.x, lightPosition.y, lightPosition.z} ;
    fragShader.setParamArrayf(
" lights[1].position " , lightp2);

    modelViewProj 
=  projMatrix  *  viewMat;
    modelViewProj 
*=  modelMat;
    
    vertShader.setColPriorMatrixf(
" modelViewProj " , ( float * )modelViewProj);      // 设置模型、视图及投影联合变换矩阵
    SetParameters();                                                                                          // 在这里设置参数

    
if (model3ds) model3ds -> render(ALL_EFFECTS);            // 画3DS模型
    vertShader.deactivate();                                                // 绘制后停掉Shader
    fragShader.deactivate();

        由于光照处理是在物体空间里进行的,所以要把World Space里的灯光位置和眼睛位置变换到物体的局部空间,这里面涉及到了模型变换矩阵计算,视矩阵计算及一系列的向量及矩阵的运算,其中用到的Matrix4f及Vector4f 及Vector3f 都是自封装的类,实现了向量及矩阵的直观的基本运算。
         (5) 程序结束要释放Cg占用的资源

CGcontext ctx  =  vertShader.getCGContext();
vertShader.destroyShader();
fragShader.destroyShader();
cgDestroyContext(ctx);                                           
// 由于同一个程序里只有一个CGContext ,多个Shader公用,只删一次

        4. 结束语:
        作为初学者的第一个练习,这个东西显得略微有点庞大,不过写这么个东西,挺有收获,一下可以了解很多东西,这里面用到的CGShader的封装还很初级,只有一般参数的设置,贴图部分及其他没接触的部分的参数设置还没考虑进去,以后会陆续完善的。终于完成了第一个比较完善的Cg光照程序。
       

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值