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

来源:互联网 发布:android 知乎日报登录 编辑:程序博客网 时间:2024/04/29 20:59

       摘要:用一个顶点光照的程序来分析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), 160100*sin(lightAngle*DEG2RAD),1);
    Vector4f lightpos2 
= Vector4f(-603505501);
    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, 
000,  010);

    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光照程序。
       

原创粉丝点击