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

时间:2021-10-25 04:37:49

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

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

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

         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 变量:       

CG 学习 (3)——片元光照(Fragment Lighting)vertShader.createShader(true"vertlighting.cg");
CG 学习 (3)——片元光照(Fragment Lighting)fragShader.createShader(
false"fraglighting.cg");
CG 学习 (3)——片元光照(Fragment Lighting)
CG 学习 (3)——片元光照(Fragment Lighting)
void AddCgParams()
CG 学习 (3)——片元光照(Fragment Lighting)CG 学习 (3)——片元光照(Fragment Lighting)
{
CG 学习 (3)——片元光照(Fragment Lighting)    vertShader.addParam(
"modelViewProj");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"globalAmbient");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"lights[0].color");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"lights[0].position");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"lights[1].color");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"lights[1].position");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"eyePosition");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"material.Ke");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"material.Ka");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"material.Ks");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"material.Kd");
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.addParam(
"material.shininess");
CG 学习 (3)——片元光照(Fragment Lighting)}

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

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

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

CG 学习 (3)——片元光照(Fragment Lighting)                     Vector4f lightpos1 = Vector4f(100*cos(lightAngle*DEG2RAD), 160100*sin(lightAngle*DEG2RAD),1);
CG 学习 (3)——片元光照(Fragment Lighting)    Vector4f lightpos2 
= Vector4f(-603505501);
CG 学习 (3)——片元光照(Fragment Lighting)    Vector4f eyepos   
= Vector4f(0,0,400,1);
CG 学习 (3)——片元光照(Fragment Lighting)    Vector4f lightPosition, eyePosition;
CG 学习 (3)——片元光照(Fragment Lighting)
CG 学习 (3)——片元光照(Fragment Lighting)    glClear(GL_COLOR_BUFFER_BIT 
| GL_DEPTH_BUFFER_BIT);    // 清除屏幕和深度缓存
CG 学习 (3)——片元光照(Fragment Lighting)
CG 学习 (3)——片元光照(Fragment Lighting)                     
//draw model and send it to CG for lighting
CG 学习 (3)——片元光照(Fragment Lighting)
    Matrix4f revModelMat, scaleMat, rotateMat, modelMat, viewMat, modleViewMat;
CG 学习 (3)——片元光照(Fragment Lighting)    viewMat.lookAt(eyepos.x,eyepos.y,eyepos.z, 
000,  010);
CG 学习 (3)——片元光照(Fragment Lighting)
CG 学习 (3)——片元光照(Fragment Lighting)    vertShader.activate();                                     
//启用Shader
CG 学习 (3)——片元光照(Fragment Lighting)
    fragShader.activate();
CG 学习 (3)——片元光照(Fragment Lighting)    modelMat.rotateX(xrot);
CG 学习 (3)——片元光照(Fragment Lighting)    rotateMat.rotateY(yrot);
CG 学习 (3)——片元光照(Fragment Lighting)    modelMat 
*= rotateMat;                                   //获得模型的模型变换矩阵
CG 学习 (3)——片元光照(Fragment Lighting)
    revModelMat = modelMat; revModelMat.setInverse();
CG 学习 (3)——片元光照(Fragment Lighting)    lightPosition 
= revModelMat * lightpos1;          //把灯光的位置变换到物体局部空间
CG 学习 (3)——片元光照(Fragment Lighting)
    eyePosition   = revModelMat * eyepos;            //把眼睛的位置变换到物体的局部空间
CG 学习 (3)——片元光照(Fragment Lighting)CG 学习 (3)——片元光照(Fragment Lighting)
    float lightp[3= {lightPosition.x, lightPosition.y, lightPosition.z};
CG 学习 (3)——片元光照(Fragment Lighting)CG 学习 (3)——片元光照(Fragment Lighting)    
float eyep[3]   = {eyePosition.x, eyePosition.y, eyePosition.z};
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.setParamArrayf(
"lights[0].position", lightp);
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.setParamArrayf(
"eyePosition", eyep);
CG 学习 (3)——片元光照(Fragment Lighting)    lightPosition 
= revModelMat * lightpos2;
CG 学习 (3)——片元光照(Fragment Lighting)CG 学习 (3)——片元光照(Fragment Lighting)    
float lightp2[3= {lightPosition.x, lightPosition.y, lightPosition.z};
CG 学习 (3)——片元光照(Fragment Lighting)    fragShader.setParamArrayf(
"lights[1].position", lightp2);
CG 学习 (3)——片元光照(Fragment Lighting)
CG 学习 (3)——片元光照(Fragment Lighting)    modelViewProj 
= projMatrix * viewMat;
CG 学习 (3)——片元光照(Fragment Lighting)    modelViewProj 
*= modelMat;
CG 学习 (3)——片元光照(Fragment Lighting)    
CG 学习 (3)——片元光照(Fragment Lighting)    vertShader.setColPriorMatrixf(
"modelViewProj", (float*)modelViewProj);     //设置模型、视图及投影联合变换矩阵
CG 学习 (3)——片元光照(Fragment Lighting)
    SetParameters();                                                                                         //在这里设置参数
CG 学习 (3)——片元光照(Fragment Lighting)

CG 学习 (3)——片元光照(Fragment Lighting)    
if(model3ds) model3ds->render(ALL_EFFECTS);           //画3DS模型
CG 学习 (3)——片元光照(Fragment Lighting)
    vertShader.deactivate();                                               //绘制后停掉Shader
CG 学习 (3)——片元光照(Fragment Lighting)
    fragShader.deactivate();

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

CG 学习 (3)——片元光照(Fragment Lighting)CGcontext ctx = vertShader.getCGContext();
CG 学习 (3)——片元光照(Fragment Lighting)vertShader.destroyShader();
CG 学习 (3)——片元光照(Fragment Lighting)fragShader.destroyShader();
CG 学习 (3)——片元光照(Fragment Lighting)cgDestroyContext(ctx);                                           
//由于同一个程序里只有一个CGContext ,多个Shader公用,只删一次

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