新手思路——phong简单光照模型绘制

第一步:熟悉opengl 编程。

制作一个会旋转的圆锥体,并加入贴图。

第二步:读取复杂物体表面的顶点信息。已知复杂物体表面是由一个个三角面片构成的三角网格图形。

读出点、线、面 信息,然后绘制顶点。

第三步:在给定模型的基础上,在取消光照情况下,首先尝试自己计算漫反射光照的值。

设定光源位置LightPosition(三维坐标)、光源颜色值LightShiness、漫反射系数值Diffuse(RGB格式),同时可由函数求出每个顶点的属性(包括顶点坐标pVertex,顶点法线向量等),然后将LightPosition与顶点坐标pVertex的各坐标轴的值想减,求得光源向量,然后与顶点法线向量求点积,得到cos值,然后利用公式I=Diffuse*cos*LightShiness求得作用于顶点的漫反射光照值I(RGB格式)。

为了简化计算,光源使用平行光。初始位置为(0,0,1)。

首先在CSceneGraph3d类中定义了光源位置、光源颜色值(即光强)、漫反射系数三个变量,并在CSceneGraph3d类的构造函数CSceneGraph3d::CSceneGraph3d()中初始化。为了能在CMesh3d类中访问到这三个变量,需要编写一个函数实现传值操作,这个函数是添加在CMesh3d类中,命名为DrawMe(GLfloat* fDiffuse,GLfloat* fLightness,GLfloat*flightpos);

然后在CSceneGraph3d类中的glDraw()函数中添加如下代码:

GLfloat m_LightPos[] = {m_lightposition.x(),m_lightposition.y(),m_lightposition.z()};

((CMesh3d*)pObject3d)->DrawMe(m_fDiffu,m_fLightness,m_LightPos);

pObject3d->glDraw();

最后在CMesh3d类中的glBuildList()函数中实现计算漫反射光照值。

第四步:光源位置不变,物体旋转。

物体旋转时,法向改变,则有多少个顶点CPU需要重复计算多少次,这样耗时巨大,所以想到另一种方法,就是在利用glRotate()进行物体旋转时求出其旋转矩阵,然后求其逆矩阵,再和光源方向做点积,这种方法保证只计算一次光源方向,提高了CPU利用率。

在CMeshView::OnTimer()函数中求旋转物体时转换矩阵的逆矩阵inv_matrix,将上面的glRotatef顺序倒着写,并使旋转角度求反,即由正变负。然后将inv_matrix传递到CMesh3D类。

传值:CMeshDoc* pDOc = GetDocument();

CMesh3d *pMesh = (CMesh3d *)pDOc->m_SceneGraph.GetAt(0);

pMesh->SendInverseMatrix(inv_matrix);

其中SendInverseMatrix 函数为:

void SendInverseMatrix(GLfloat *inv_matrix)

{

memcpy(m_fInvMatrix, inv_matrix, 16 * sizeof(GLfloat));//内存拷贝

}

第五步:让光源位置移动,使其围绕物体旋转

在CSceneGraph3d类中添加成员函数spinDisplay()使光源绕Y轴旋转,则 x 、z 值改变。然后在CMeshView::OnTimer()中调用此函数。

为了清晰地看到光源是如何移动的,绕着什么方向移动的,我设置了一条红色的线(从原点到光源位置),当光源绕y轴旋转时若从正前方看模型,则此条线在屏幕上会显示为一条直线,所以我将平面(x,0,z)转换为(x,-z,0)平面,即是将俯视图向上旋转90度,使我们能在屏幕正前方看到俯视图,此时绘制的线移动时显示为移动一个圆周。

glColor3f(1,0,0);

glBegin(GL_LINES);

glVertex3f(0,0,0);

glVertex3f(10*m_light_parallel.x(),-10*m_light_parallel.z(),0);//保证看到俯视图

glEnd();

第六步:计算镜面反射光照,最后通过数学计算公式,在计算机上编程实现phong模型(简单光照模型)。

在使用phong模型时不考虑环境光,因为环境光对物体的影响很小,可以忽略。所以最终颜色是漫反射光照和镜面反射光照构成的phong模型。

CVector3d pv = *pVector; // 赋值,得到CVector3d类型的pv,而非CVector3d*类型!

pv.NormalizeL2(1); //法向单位化,归一化

GLfloat fCos = (GLfloat)Scalar(&pv,&m_light_parallel); //点积求得cos角

if (fCos<0.0f) fCos=0.0f;

GLfloat fXX1 = m_fDiffuse[0] * m_fLightness[0]*fCos;

GLfloat fYY1 = m_fDiffuse[1] * m_fLightness[1]*fCos;

GLfloat fZZ1 = m_fDiffuse[2] * m_fLightness[2]*fCos;

GLfloat m_specular[3]={0.7,0.7,0.7}; //镜面反射系数

CVector3d m_eye; //观察方向

m_eye.Set(0.0,0.0,1.0);//从屏幕里面指向外面,为Z轴正向

GLfloat L_V; //光源方向和观察方向

L_V=sqrt(pow(m_eye.x()+m_light_parallel.x(),2) +

pow(m_eye.y()+m_light_parallel.y(),2) +

pow(m_eye.y()+m_light_parallel.y(),2) );

CVector3d m_H_specular; // 公式 H=(L+V)/ | L+V |

m_H_specular.Set((m_light_parallel.x()+m_eye.x())/L_V,

(m_light_parallel.y()+m_eye.y())/L_V,

(m_light_parallel.z()+m_eye.z())/L_V);

m_H_specular.NormalizeL2(1);

GLfloat V_R=(GLfloat)Scalar(&m_H_specular,&pv); //求点积

if (V_R<0.0f) V_R=0.0f;

GLfloat n=5;//镜面高光系数 (0--2000) n越大,光斑越暗

GLfloat m_V_R=pow(V_R,n); // V_R的n次方

GLfloat fXX2=m_fLightness[0]*m_specular[0]*m_V_R;

GLfloat fYY2=m_fLightness[1]*m_specular[1]*m_V_R;

GLfloat fZZ2=m_fLightness[2]*m_specular[2]*m_V_R;

GLfloat fXX= fXX1+fXX2;

GLfloat fYY=fYY1+fYY2;

GLfloat fZZ=fZZ1+fZZ2;

if (fXX>255) fXX=255;

if (fYY>255) fYY=255;

if (fZZ>255) fZZ=255;

::glColor3ub(fXX,fYY,fZZ);

你可能感兴趣的:(opengl)