一、实验项目要求
任务1:使用均布网格细分曲面
任务2:使用黑白棋盘颜色填充细分后的小面。
二、理论分析或算法分析
在双三次贝塞尔曲面算法中,绘制出面,双的面颜色设置为黑色,单的面绘制成白色,重新写一个函数来构成面的绘画,因为是间隔的,所以然后设一个i和j,i和j相加对2的求余,如果可以除尽就是填充黑色,如果没有除尽就是填充白色;
使用均布网格细分曲面:
采用非递归,然后实现步长为0.1,将曲面划分为10*10的网格,然后将四个顶点连接成一个曲面;double tStep = 0.1;//步长
设置100个网格,CP2 gridP2[11][11]; //100个平面网格
采用斜投影:gridP2[ROUND(u*10)][ROUND(v*10)] = projection.CavalierProjection(pt);//斜投影
填充颜色设置:黑色:CBrush brushBlack(RGB(0, 0, 0));
白色: CBrush brushWhite(RGB(255, 255, 255));
绘制网格并填充:for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
if ((i + j) % 2 == 0)
pDC->SelectObject(&brushBlack);
else
pDC->SelectObject(brushWhite);
pDC->BeginPath();
pDC->MoveTo(ROUND(gridP2[i][j].x), ROUND(gridP2[i][j].y));
pDC->LineTo(ROUND(gridP2[i + 1][j].x), ROUND(gridP2[i + 1][j].y));
pDC->LineTo(ROUND(gridP2[i + 1][j + 1].x), ROUND(gridP2[i + 1][j + 1].y));
pDC->LineTo(ROUND(gridP2[i][j + 1].x), ROUND(gridP2[i][j + 1].y));
pDC->LineTo(ROUND(gridP2[i][j].x), ROUND(gridP2[i][j].y));
pDC->EndPath();
pDC->StrokeAndFillPath();
}
在这块为了实现对边界的颜色绘制,这里要使用pDC->StrokeAndFillPath();如果没有使用这个函数,就会出现边界不是框内的颜色;
使用MoveTo和LineTo实现对网格的绘画;
控制网格的绘制:bezier.DrawControlGrid(pDC);
三、实现方法
代码实现:
绘制网格颜色填充:
void CBicubicBezierPatch::DrawCurvedPatch(CDC* pDC)
{
double M[4][4];//系数矩阵M
M[0][0]=-1,M[0][1]=3, M[0][2]=-3,M[0][3]=1;
M[1][0]= 3,M[1][1]=-6,M[1][2]= 3,M[1][3]=0;
M[2][0]=-3,M[2][1]=3, M[2][2]= 0,M[2][3]=0;
M[3][0]=1, M[3][1]=0, M[3][2]= 0,M[3][3]=0;
CP3 P3[4][4];//曲线计算用控制点数组
for(int i=0;i < 4;i++)
for(int j = 0;j < 4;j++)
P3[i][j] = P[i][j];
LeftMultiplyMatrix(M, P3);//系数矩阵左乘三维点矩阵
TransposeMatrix(M);//计算转置矩阵
RightMultiplyMatrix(P3, M);//系数矩阵右乘三维点矩阵
double tStep = 0.1;//步长
double u0,u1,u2,u3,v0,v1,v2,v3;//u,v参数的幂
CP2 gridP2[11][11]; //100个平面网格
for(double u = 0;u <= 1;u += tStep)
for(double v = 0;v <= 1;v += tStep)
{
u3 = u * u * u, u2 = u * u, u1 = u, u0 = 1;
v3 = v * v * v, v2 = v * v, v1 = v, v0 = 1;
CP3 pt = (u3 * P3[0][0] + u2 * P3[1][0] + u1 * P3[2][0] + u0 * P3[3][0]) * v3
+(u3 * P3[0][1] + u2 * P3[1][1] + u1 * P3[2][1] + u0 * P3[3][1]) * v2
+(u3 * P3[0][2] + u2 * P3[1][2] + u1 * P3[2][2] + u0 * P3[3][2]) * v1
+(u3 * P3[0][3] + u2 * P3[1][3] + u1 * P3[2][3] + u0 * P3[3][3]) * v0;
CP2 Point2 = projection.CavalierProjection(pt);//斜投影
if (0 == v)
pDC->MoveTo(ROUND(Point2.x), ROUND(Point2.y));
else
pDC->LineTo(ROUND(Point2.x), ROUND(Point2.y));
gridP2[ROUND(u*10)][ROUND(v*10)] =
projection.CavalierProjection(pt);//斜投影
CBrush brushBlack(RGB(0, 0, 0));
CBrush brushWhite(RGB(255, 255, 255));
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
if ((i + j) % 2 == 0)
pDC->SelectObject(&brushBlack);
else
pDC->SelectObject(brushWhite);
pDC->BeginPath();
pDC->MoveTo(ROUND(gridP2[i][j].x),
ROUND(gridP2[i][j].y));
pDC->LineTo(ROUND(gridP2[i + 1][j].x),
ROUND(gridP2[i + 1][j].y));
pDC->LineTo(ROUND(gridP2[i + 1][j + 1].x),
ROUND(gridP2[i + 1][j + 1].y));
pDC->LineTo(ROUND(gridP2[i][j + 1].x),
ROUND(gridP2[i][j + 1].y));
pDC->LineTo(ROUND(gridP2[i][j].x),
ROUND(gridP2[i][j].y));
pDC->EndPath();
pDC->StrokeAndFillPath();
}
}
for(double v = 0;v <= 1;v += tStep)
for(double u = 0;u <= 1;u += tStep)
{
u3 = u * u * u; u2 = u * u; u1 = u; u0 = 1;
v3 = v * v * v; v2 = v * v; v1 = v; v0 = 1;
CP3 pt = (u3 * P3[0][0] + u2 * P3[1][0] + u1 * P3[2][0] + u0 * P3[3][0]) * v3
+(u3 * P3[0][1] + u2 * P3[1][1] + u1 * P3[2][1] + u0 * P3[3][1]) * v2
+(u3 * P3[0][2] + u2 * P3[1][2] + u1 * P3[2][2] + u0 * P3[3][2]) * v1
+(u3 * P3[0][3] + u2 * P3[1][3] + u1 * P3[2][3] + u0 * P3[3][3]) * v0;
CP2 Point2 = projection.CavalierProjection(pt);//斜投影
if(0 == u)
pDC->MoveTo(ROUND(Point2.x), ROUND(Point2.y));
else
pDC->LineTo(ROUND(Point2.x),ROUND(Point2.y));
}
}
双三次贝塞尔曲面的实现:
#pragma once
#include "Projection.h"
class CBicubicBezierPatch
{
public:
CBicubicBezierPatch(void);
virtual ~CBicubicBezierPatch(void);
void ReadControlPoint(CP3 P[4][4]);//读入16个控制点
void DrawCurvedPatch(CDC* pDC);//绘制双三次Bezier曲面片
//void DrawControlGrid(CDC* pDC);//绘制控制网格
private:
void LeftMultiplyMatrix(double M[4][4],CP3 P[4][4]);//左乘顶点矩阵
void RightMultiplyMatrix(CP3 P[4][4],double M[4][4]);//右乘顶点矩阵
void TransposeMatrix(double M[4][4]);//转置矩阵
public:
CP3 P[4][4];//三维控制点
CProjection projection;//投影对象
};
#include "pch.h"
#include "BicubicBezierPatch.h"
#define ROUND(d) int(d + 0.5)
CBicubicBezierPatch::CBicubicBezierPatch(void)
{
}
CBicubicBezierPatch::~CBicubicBezierPatch(void)
{
}
void CBicubicBezierPatch::ReadControlPoint(CP3 P[4][4])
{
for(int i = 0;i < 4;i++)
for(int j = 0;j < 4;j++)
this->P[i][j] = P[i][j];
}
void CBicubicBezierPatch::DrawCurvedPatch(CDC* pDC)
{
double M[4][4];//系数矩阵M
M[0][0]=-1,M[0][1]=3, M[0][2]=-3,M[0][3]=1;
M[1][0]= 3,M[1][1]=-6,M[1][2]= 3,M[1][3]=0;
M[2][0]=-3,M[2][1]=3, M[2][2]= 0,M[2][3]=0;
M[3][0]=1, M[3][1]=0, M[3][2]= 0,M[3][3]=0;
CP3 P3[4][4];//曲线计算用控制点数组
for(int i=0;i < 4;i++)
for(int j = 0;j < 4;j++)
P3[i][j] = P[i][j];
LeftMultiplyMatrix(M, P3);//系数矩阵左乘三维点矩阵
TransposeMatrix(M);//计算转置矩阵
RightMultiplyMatrix(P3, M);//系数矩阵右乘三维点矩阵
double tStep = 0.1;//步长
double u0,u1,u2,u3,v0,v1,v2,v3;//u,v参数的幂
CP2 gridP2[11][11]; //100个平面网格
for(double u = 0;u <= 1;u += tStep)
for(double v = 0;v <= 1;v += tStep)
{
u3 = u * u * u, u2 = u * u, u1 = u, u0 = 1;
v3 = v * v * v, v2 = v * v, v1 = v, v0 = 1;
CP3 pt = (u3 * P3[0][0] + u2 * P3[1][0] + u1 * P3[2][0] + u0 * P3[3][0]) * v3
+(u3 * P3[0][1] + u2 * P3[1][1] + u1 * P3[2][1] + u0 * P3[3][1]) * v2
+(u3 * P3[0][2] + u2 * P3[1][2] + u1 * P3[2][2] + u0 * P3[3][2]) * v1
+(u3 * P3[0][3] + u2 * P3[1][3] + u1 * P3[2][3] + u0 * P3[3][3]) * v0;
CP2 Point2 = projection.CavalierProjection(pt);//斜投影
if (0 == v)
pDC->MoveTo(ROUND(Point2.x), ROUND(Point2.y));
else
pDC->LineTo(ROUND(Point2.x), ROUND(Point2.y));
gridP2[ROUND(u*10)][ROUND(v*10)] = projection.CavalierProjection(pt);//斜投影
CBrush brushBlack(RGB(0, 0, 0));
CBrush brushWhite(RGB(255, 255, 255));
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
if ((i + j) % 2 == 0)
pDC->SelectObject(&brushBlack);
else
pDC->SelectObject(brushWhite);
pDC->BeginPath();
pDC->MoveTo(ROUND(gridP2[i][j].x), ROUND(gridP2[i][j].y));
pDC->LineTo(ROUND(gridP2[i + 1][j].x), ROUND(gridP2[i +
1][j].y));
pDC->LineTo(ROUND(gridP2[i + 1][j + 1].x), ROUND(gridP2[i + 1][j + 1].y));
pDC->LineTo(ROUND(gridP2[i][j + 1].x), ROUND(gridP2[i][j + 1].y));
pDC->LineTo(ROUND(gridP2[i][j].x), ROUND(gridP2[i][j].y));
pDC->EndPath();
pDC->StrokeAndFillPath();
}
}
for(double v = 0;v <= 1;v += tStep)
for(double u = 0;u <= 1;u += tStep)
{
u3 = u * u * u; u2 = u * u; u1 = u; u0 = 1;
v3 = v * v * v; v2 = v * v; v1 = v; v0 = 1;
CP3 pt = (u3 * P3[0][0] + u2 * P3[1][0] + u1 * P3[2][0] + u0 * P3[3][0]) * v3
+(u3 * P3[0][1] + u2 * P3[1][1] + u1 * P3[2][1] + u0 * P3[3][1]) * v2
+(u3 * P3[0][2] + u2 * P3[1][2] + u1 * P3[2][2] + u0 * P3[3][2]) * v1
+(u3 * P3[0][3] + u2 * P3[1][3] + u1 * P3[2][3] + u0 * P3[3][3]) * v0;
CP2 Point2 = projection.CavalierProjection(pt);//斜投影
if(0 == u)
pDC->MoveTo(ROUND(Point2.x), ROUND(Point2.y));
else
pDC->LineTo(ROUND(Point2.x),ROUND(Point2.y));
}
}
void CBicubicBezierPatch::LeftMultiplyMatrix(double M[4][4],CP3 P[4][4])//左乘矩阵M*P
{
CP3 PTemp [4][4];//临时矩阵
for(int i = 0;i < 4;i++)
for(int j = 0;j < 4;j++)
PTemp [i][j] = M[i][0] * P[0][j] + M[i][1] * P[1][j] + M[i][2] * P[2][j] + M[i][3] * P[3][j];
for(int i = 0;i < 4;i++)
for(int j =0;j < 4;j++)
P[i][j] = PTemp [i][j];
}
void CBicubicBezierPatch::RightMultiplyMatrix(CP3 P[4][4],double M[4][4])//右乘矩阵P*M
{
CP3 PTemp [4][4];//临时矩阵
for(int i = 0;i < 4;i++)
for(int j = 0;j < 4;j++)
PTemp [i][j] = P[i][0] * M[0][j] + P[i][1] * M[1][j] + P[i][2] * M[2][j] + P[i][3] * M[3][j];
for(int i=0;i < 4;i++)
for(int j=0;j < 4;j++)
P[i][j] = PTemp [i][j];
}
void CBicubicBezierPatch::TransposeMatrix(double M[4][4])//转置矩阵
{
double PTemp[4][4];//临时矩阵
for(int i = 0;i < 4;i++)
for(int j = 0;j < 4;j++)
PTemp[j][i] = M[i][j];
for(int i = 0;i < 4;i++)
for(int j = 0;j < 4;j++)
M[i][j] = PTemp[i][j];
}
控制网格的绘制:
//void CBicubicBezierPatch::DrawControlGrid(CDC* pDC)//绘制控制网格
//{
// CP2 P2[4][4];//二维控制点
// for(int i = 0;i < 4;i++)
// for(int j = 0;j < 4;j++)
// P2[i][j] = projection.CavalierProjection(P[i][j]);
// CPen NewPen,*pOldPen;
// NewPen.CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
// pOldPen=pDC->SelectObject(&NewPen);
// for(int i = 0; i < 4; i++)
// {
// pDC->MoveTo(ROUND(P2[i][0].x), ROUND(P2[i][0].y));
// for(int j = 1;j < 4;j++)
// pDC->LineTo(ROUND(P2[i][j].x), ROUND(P2[i][j].y));
// }
// for(int j = 0;j < 4;j++)
// {
// pDC->MoveTo(ROUND(P2[0][j].x), ROUND(P2[0][j].y));
// for(int i = 1;i < 4;i++)
// pDC->LineTo(ROUND(P2[i][j].x), ROUND(P2[i][j].y));
// }
// pDC->SelectObject(pOldPen);
// NewPen.DeleteObject();
//}
四、实验结果分析
实验结果
出现的问题
1.命名不正确,导致出现问题,这是细节问题
2.刚开始进行填充颜色的时候,没有懂要将一个曲面细分为10*10的网格,到后来看了视频之后,要使用步长来构造一个闭合的四边形,然后使用路径层填充,在这里需要注意的是pDC->BeginPath();和pDC->EndPath();是成对出现,最后要使用这个函数pDC->StrokeAndFillPath();,否则会出现线上不是它要填充的颜色,会出现分界线;
3.在进行填充颜色的使用for循环来判断单个来填充黑色,双个来填充白色,这是一个逻辑问题;
4.曲面的形状是由控制网格的控制点的来控制的。