⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lighting.cpp

📁 计算机图形学原理及算法教程(VC++版)程序代码
💻 CPP
字号:
/////////////////////////////////////////////////////////////////////////////////
//	
// Lighting.cpp: implementation of the CLighting class.
#include "stdafx.h"
#include "Lighting.h"

//#include "FloatColor.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CLighting::CLighting()
{
	m_clrGlobalAmb.red  = 0.2f;		m_clrGlobalAmb.green = 0.2f;
	m_clrGlobalAmb.blue = 0.2f;		m_clrGlobalAmb.alpha = 1.0f;
	
	//缺省值: 无穷远视点模型
	m_bLocalViewer = FALSE;

	//缺省值:只照射前面
	m_bTwoSides = FALSE;
}

CLighting::~CLighting()
{
}

//设置光照模型
void CLighting::LightModelb(DWORD dwProperty,  BOOL bParam)
{
	if(dwProperty == G3D_LIGHT_MODEL_LOCAL_VIEWER)
		m_bLocalViewer = bParam;
	else if(dwProperty == G3D_LIGHT_MODEL_TWO_SIDE)
		m_bTwoSides = bParam;
}

//设置全局环境光
void CLighting::LightModelfv(DWORD dwProperty,  const float* pfParams)
{
	if(dwProperty == G3D_LIGHT_MODEL_AMBIENT)
	{
		m_clrGlobalAmb.red   = pfParams[0];
		m_clrGlobalAmb.green = pfParams[1];
		m_clrGlobalAmb.blue  = pfParams[2];
		m_clrGlobalAmb.alpha = pfParams[3];
	}
}

//光照计算
//pObj---------被光照的物体    (单个物体)
//material-----物体标准材质
//viewer-------视点, 用于光照计算, 被定义于世界坐标系
//lights-------多光源
//nNumLight----光源个数
void CLighting::Lighting(CObject3d* pObj,  const CMaterial& material,  VERTEX3D viewer,  const CLightObj* lights,  int nNumLight)
{
	//注:   该光照计算在世界坐标系中完成

	ASSERT(pObj);
	ASSERT(lights);

	//显式表示视点, 它被定义于世界坐标系
	float xViewer = viewer.x,   yViewer = viewer.y,   zViewer = viewer.z;

	//定义一个无穷远视点观察向量, 
	//
	CVector3d vLocalView(xViewer,  yViewer,  zViewer);

	//如果计算高光时采用无穷远视点, 则现在就单位化, 这将减少大量的计算
	
	if(!m_bLocalViewer)
		vLocalView.Unitize();

	
	//物体顶点个数
	int nNumVertex = pObj->GetVertexListSize();

	//开始计算
	// i 按物体顶点个数计算循环,  j 则按光源个数进行循环
	for(int i = 0; i < nNumVertex; i++)
	{
		
		//获取物体的第 i 个顶点:
		float xVertex = pObj->m_vList[i].m_coord.x;
		float yVertex = pObj->m_vList[i].m_coord.y;
		float zVertex = pObj->m_vList[i].m_coord.z;
		float wVertex = pObj->m_vList[i].m_coord.w;

		//将齐次坐标转化为真实坐标
		//无穷远点, 近似地用 0.0001 来代替(扩大齐次坐标10000倍的点来代替无穷远点的颜色)
		//不必考虑 wVertex 趋近于 0 时的符号
		if(ABS(wVertex) < 0.0001f)wVertex = 0.0001f;
		
		xVertex /= wVertex;
		yVertex /= wVertex;
		zVertex /= wVertex;

		//顶点最后的颜色
		//第一项, 物体辐射颜色, 这是直接贡献项
		float fLastR = material.m_clrEmi.red;
		float fLastG = material.m_clrEmi.green;
		float fLastB = material.m_clrEmi.blue;
		
		//全局环境光的贡献
		//全局环境光 * 材质对环境光的反射系数 ----- 
		//分量对应相乘后, 累积至最后的颜色之中.
		fLastR += (m_clrGlobalAmb.red * material.m_refAmb.red);
		fLastG += (m_clrGlobalAmb.green * material.m_refAmb.green);
		fLastB += (m_clrGlobalAmb.blue * material.m_refAmb.blue);

		//来自于光源的贡献
		float fr,  fg,  fb;

		for(int j = 0; j < nNumLight; j++)
		{
			//如果光源是开启的
			if(lights[j].m_bOnOff)
			{
				//其一, 计算衰减系数

				//衰减系数.等于1.0则不衰减
				float fAttenuCoef = 1.0f;
				
				//获取第 j 个光源位置
				float xLight = lights[j].m_hcPosition.x;
				float yLight = lights[j].m_hcPosition.y;
				float zLight = lights[j].m_hcPosition.z;
				float wLight = lights[j].m_hcPosition.w;


				//只有点光源我们才认为它是可衰减的, 此时, w = 1.0f.
				//如果光源是平行光源, 即定向光源, 则不衰减, 时时, w = 0.0f

				//定位光源
				if(ABS(wLight) > 0.0001f)
				{
					//真实位置, 保持齐次坐标分量不变
					xLight /= wLight;
					yLight /= wLight;
					zLight /= wLight;
					
					//常数衰减系数
					fAttenuCoef = lights[j].m_fAttCoef0;
					
					//一次衰减系数与二次衰减系数
					float fc1 = lights[j].m_fAttCoef1;
					float fc2 = lights[j].m_fAttCoef2;
					if((fc1 > 0.0001f) || (fc2 > 0.0001f))
					{
						//求顶点至光源的距离
						float fDist = (float)sqrt((xLight - xVertex) * (xLight - xVertex) +
										   (yLight - yVertex) * (yLight - yVertex) +
										   (zLight - zVertex) * (zLight - zVertex) );
					
						//加入一次和二次因子
						fAttenuCoef += (fc1 * fDist + fc2 * fDist * fDist);
					}
					
					if(fAttenuCoef < 0.0001f) fAttenuCoef = 0.0001f;
					fAttenuCoef = 1.0f / fAttenuCoef;

					//衰减系数不得大于1.0
					fAttenuCoef = MIN(1.0f,  fAttenuCoef);
				
				}
				
				//计算聚光因子

				//聚光因子, 一般点光源的聚光因子为 1.0f, (发散半角为180度)
				float fSpotFactor = 1.0f;
				
				//发散半角
				float fCutoff = lights[j].m_fSpotCutoff;

				//聚光产生的条件:第一, 光为定位光源; 第二, 光的发散半角小于或等于90度
				if((ABS(wLight) > 0.0001f) && (fCutoff < 90.0001f))
				{
					//光源的真实位置已经为(xLight,  yLight,  zLight), 
					//定向光源不产生聚光效果
					
					//向量: 聚光位置指向照射顶点
					CVector3d vLightToVertex((xVertex - xLight),  (yVertex - yLight),  (zVertex - zLight));
					
					//单位化
					vLightToVertex.Unitize();

					//聚光照射方向(已经是一个单位向量) 与 向量 vLightToVertex 夹角的余弦
					float fInsideCone = vLightToVertex.Dot(lights[j].m_spotDirection);

					//如果顶点位于光锥之外, 则不会有聚光光线照射到物体上
					if(fInsideCone < (float)cos((fCutoff * PIE) / 180.0))
						fSpotFactor = 0.0f;
					else
					{
						//利用聚光指数进行计算
						fSpotFactor = (float)pow(fInsideCone,  lights[j].m_fSpotExp);
					}
				
				}//enf if ---- for calculating spot factor
				

				// 计算来自光源的贡献(现在已经有足够的条件了)
				
				//加入环境反射部分:
				fr = material.m_refAmb.red * lights[j].m_clrAmb.red;
				fg = material.m_refAmb.green * lights[j].m_clrAmb.green;
				fb = material.m_refAmb.blue * lights[j].m_clrAmb.blue;

				//其次, 计算漫反射部分
				
				//顶点指向光源的向量
				CVector3d vVertexToLight((xLight - xVertex),  (yLight - yVertex),  (zLight - zVertex)); 
				
				//如果光源为平行光源(定位光源)
				if((ABS(wLight) <= 0.0001f))
				{
					//光源的位置就是照射方向, 因而顶点至光源的向量就是光源位置向量的相反向量
					vVertexToLight.x = -xLight;
					vVertexToLight.y = -yLight;
					vVertexToLight.z = -zLight;
				}
				
				//单位化
				vVertexToLight.Unitize();

				//顶点法线向量与 vVertexToLight 向量的夹角的余弦
				//顶点法线应是单位向量, 这在建模时已经或必须保证的
				float fCos = vVertexToLight.Dot(pObj->m_vList[i].m_normal);
				
				fCos = (fCos < 0.0f) ? 0.0f : fCos;

				//加入漫反射部分的贡献
				fr += (material.m_refDif.red * lights[j].m_clrDif.red * fCos);
				fg += (material.m_refDif.green * lights[j].m_clrDif.green * fCos);
				fb += (material.m_refDif.blue * lights[j].m_clrDif.blue * fCos);

				// 计算高光部分

				//如果顶点法线与顶点至光源的向量夹角大于90度, 则没有高光产生
				//否则就产生
				if(fCos > 0.0f)
				{
					//计算顶点指向视点的单位向量:
					CVector3d vVertexToViewer;
					
					//无穷远视点模型
					if(!m_bLocalViewer)
					{
						vVertexToViewer = vLocalView;
					}
					//局部视点模型
					else
					{
						vVertexToViewer.x = (xViewer - xVertex);
						vVertexToViewer.y = (yViewer - yVertex);
						vVertexToViewer.z = (zViewer - zVertex);
						//单位化:
						vVertexToViewer.Unitize();
					}
					
					//加上顶点指向光源的单位向量:
					vVertexToViewer += vVertexToLight;
					
					//再次单位化:
					vVertexToViewer.Unitize();

					//光度因子基数:与顶点法线作点积
					float fShine = vVertexToViewer.Dot(pObj->m_vList[i].m_normal);
					fShine = (fShine < 0.0f) ? 0.0f : fShine;
					float fShineFactor = (float)pow(fShine,  material.m_fShininess);
					
					//加入高光部分的贡献
					fr += (material.m_refSpe.red * lights[j].m_clrSpe.red * fShineFactor);
					fg += (material.m_refSpe.green * lights[j].m_clrSpe.green * fShineFactor);
					fb += (material.m_refSpe.blue * lights[j].m_clrSpe.blue * fShineFactor);
				}// end if(fCos > 0.0f)
				
				//最后乘以衰减和聚光因子,第 j 个光对物体的第个顶点的照射:
				fr *= (fAttenuCoef * fSpotFactor);
				fg *= (fAttenuCoef * fSpotFactor);
				fb *= (fAttenuCoef * fSpotFactor);

				//累加至最后颜色:
				fLastR += fr;
				fLastG += fg;
				fLastB += fb;
			}// end if(lights[0].m_bOnOff)
		}//end j (number of lights)
		
		//作颜色归一化处理:
		fLastR = (fLastR < 0.0f) ? 0.0f : ((fLastR > 1.0f) ? 1.0f : fLastR);
		fLastG = (fLastG < 0.0f) ? 0.0f : ((fLastG > 1.0f) ? 1.0f : fLastG);
		fLastB = (fLastB < 0.0f) ? 0.0f : ((fLastB > 1.0f) ? 1.0f : fLastB);

		//颜色赋值:
		pObj->m_vList[i].m_clr.red = fLastR;
		pObj->m_vList[i].m_clr.green = fLastG;
		pObj->m_vList[i].m_clr.blue = fLastB;

		//对于alpha, 这里简单地用材质漫反射属性来代替
		pObj->m_vList[i].m_clr.alpha = material.m_refDif.alpha;

	}

}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -