📄 prender.cpp
字号:
/////////////////////////////////////////////////////////////////////////////////
//
// PRender.cpp: implementation of the CPRender class.
//
////////////////////////////////////////////////////////////////////////////////
// 版权所有(2002)
// Copyright(2002)
// 编写者: 向世明
// Author: Xiang Shiming
#include "stdafx.h"
#include "PRender.h"
#include "ViewFinder.h"
#include "SVisibility.h"
#include "PLine.h"
#include "PTriangle.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CPRender::CPRender()
{}
CPRender::~CPRender()
{}
//通用着色工具: 在此完成主要工作
//pDC---------设备描述表;
//pObj---------三维物体对象 (单个物体)
//viewer------视点(定义于世界坐标系)
//viewFinder--取景器
//dwRop-------着色模式
void CPRender::Render(CDC* pDC, CObject3d* pObj, VERTEX3D viewer, VIEWFINDER viewFinder, DWORD dwRop)
{
//第一步, 获取几何元素的个数
//物体顶点的个数
int nNumVertex = pObj->GetVertexListSize();
//存储将世界坐标变换为观察坐标后的结果
HOMOCOORD* pvInView = new HOMOCOORD[nNumVertex];
if(pvInView == NULL)
{
::AfxMessageBox("内存分配失败!");
exit(1);
}
//////////////////////////////////////////
//第二步:完成从世界坐标到观察坐标的变换
//观察矩阵(初始化为单位矩阵)
CMatrix3d mWatch;
//获取观察矩阵
mWatch.Watch(viewer.x, viewer.y, viewer.z);
int i;
//将世界坐标系的点变换为观察坐标系的点
for(i = 0; i < nNumVertex; i++)
{
//获取一个顶点的齐次坐标
//将顶点变换至到获取观察坐标
pvInView[i] = mWatch.Transform(pObj->m_vList[i].m_coord);
}//end for i (1st)
//
//////////////////////////////////////////
//第三步:完成从观察坐标到屏幕坐标的变换
//中间省略了如下几个步骤:
//其一, 透视剪切;其二, 设备坐标规格化
//保存变换之结果
POINT* pPtOnScrn = new POINT[nNumVertex];
if(pPtOnScrn == NULL)
{
::AfxMessageBox("内存分配失败!");
exit(1);
}
//设置视口变换工具.
CViewFinder vf(viewFinder);
//执行缩减的透视变换和视口变换
for(i = 0; i < nNumVertex; i++)
pPtOnScrn[i] = vf.Perspective(pvInView[i]);
//第四步, 绘制点模型
if(dwRop == G3D_RENDER_VERTICES)
{
//所有点均绘制
Vertexize(pDC, pObj, pPtOnScrn);
delete[] pPtOnScrn;
delete[] pvInView;
return;
}
//第五步, 测试面的可见性
//物体面的可见性将在这里获得改变
//面的可见性测试, 该测试器仅能作用于凸体模型
CSVisibility sv;
//小面个数
int nNumFacet = pObj->GetFacetListSize();
//面的可见性,
for(i = 0; i < nNumFacet; i++)
{
ASSERT(pObj->m_sList[i].m_avIndex.GetSize() > 2);
//这些点在世界坐标系中按右手规则排列
//获取组成小面的基点序号, 取前三个点作测试
int n1 = pObj->m_sList[i].m_avIndex[0];
int n2 = pObj->m_sList[i].m_avIndex[1];
int n3 = pObj->m_sList[i].m_avIndex[2];
//计算小面的可见性, 并进行记录
BOOL bVisible = sv.IsVisible(pvInView[n1], pvInView[n2], pvInView[n3]);
pObj->m_sList[i].m_bVisibility = bVisible;
}//end for i (3th)
//第六步, 绘制实体型
if(dwRop == G3D_RENDER_FLAT)
{
Flat(pDC, pObj, pPtOnScrn);
delete[] pPtOnScrn;
delete[] pvInView;
return;
}
else if(dwRop == G3D_RENDER_GOURAUD_SMOOTH)
{
Gouraud(pDC, pObj, pPtOnScrn);
delete[] pPtOnScrn;
delete[] pvInView;
return;
}
//第七步, 边的可见性(边的可见性得到改变)
//判断隐藏线算法如下:
//如果与该边相边的面中有一个是可见的, 则该边可见.
//边数
int nNumEdge = pObj->GetEdgeListSize();
//计算边的可见性
for(i = 0; i < nNumEdge; i++)
{
//注意, 我们只处理正则模型, 因为该类只能着色凸体模型
//获取共享该边的小面个数
int nNumInFacet = pObj->m_eList[i].m_asIndex.GetSize();
//孤边
if(nNumInFacet == 0)pObj->m_eList[i].m_bVisibility = TRUE;
//边界边
else if(nNumInFacet == 1)
{
int n1 = pObj->m_eList[i].m_asIndex[0];
//对于边界边, 如果其所在的小面是可见的, 则该边也是可见的
//如果不可见, 该边也不可见.
pObj->m_eList[i].m_bVisibility = pObj->m_sList[n1].m_bVisibility;
}
else if(nNumInFacet == 2)
{
//获取边所在小面的序号
int n1 = pObj->m_eList[i].m_asIndex[0];
int n2 = pObj->m_eList[i].m_asIndex[1];
//如果两个小面中有一个是可见的, 则该边是可见的
pObj->m_eList[i].m_bVisibility = (pObj->m_sList[n1].m_bVisibility ||
pObj->m_sList[n2].m_bVisibility);
}
//else
//{
//请按你自己的模型进行处理
//}
}// end for i 4th
//第八步, 绘制线框模型
if(dwRop == G3D_RENDER_WIRE)
{
FlatWirize(pDC, pObj, pPtOnScrn);
delete[] pPtOnScrn;
delete[] pvInView;
return;
}
else if(dwRop == G3D_RENDER_WIRE_LIGHTED)
{
GouraudWirize(pDC, pObj, pPtOnScrn);
delete[] pPtOnScrn;
delete[] pvInView;
return;
}
delete[] pPtOnScrn;
delete[] pvInView;
}
//绘制点模型
//pDC---------设备描述表;
//pObj---------三维物体对象
//pPtOnScrn---最终透视到屏幕上的点
void CPRender::Vertexize(CDC* pDC, CObject3d* pObj, POINT* pPtOnScrn)
{
//顶点个数
int nNumPt = pObj->GetVertexListSize();
//绘制点模型
for(int i = 0; i < nNumPt; i++)
{
//颜色(R, G, B)
BYTE byR = (BYTE)(pObj->m_vList[i].m_clr.red * 255.0f);
BYTE byG = (BYTE)(pObj->m_vList[i].m_clr.green * 255.0f);
BYTE byB = (BYTE)(pObj->m_vList[i].m_clr.blue * 255.0f);
//绘制单个点
pDC->SetPixelV(pPtOnScrn[i], RGB(byR, byG, byB));
}
}
//绘制单色线框模型(这里指, 一条边的颜色是相同的)
//pDC---------设备描述表;
//pObj---------三维物体对象
//pPtOnScrn---最终透视到屏幕上的点
void CPRender::FlatWirize(CDC* pDC, CObject3d* pObj, POINT* pPtOnScrn)
{
//物体边数
int nNumEdge = pObj->GetEdgeListSize();
//由顶点决定边的颜色, 还是由外部决定边的颜色
BOOL bAutoColor = pObj->GetAutoColorProperty();
//绘制模型
for(int i = 0; i < nNumEdge; i++)
{
//绘制边----单色线框模型
if(pObj->m_eList[i].m_bVisibility)
{
//获取边的顶点索引
int n1 = pObj->m_eList[i].m_nStart;
int n2 = pObj->m_eList[i].m_nEnd;
//边的颜色
BYTE byR, byG, byB;
//获取顶点颜色
if(bAutoColor)
{
//用第一个顶点的颜色来代替整条边的颜色
FLOATCOLORRGBA rgba = pObj->m_vList[n1].m_clr;
//顶点颜色归一化应该在外部进行
byR = (BYTE)(rgba.red * 255.0f);
byG = (BYTE)(rgba.green * 255.0f);
byB = (BYTE)(rgba.blue * 255.0f);
}
//获取边的颜色
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -