我将Nurbs曲面数据点读入后,画曲面没有问题,但在增加裁剪过程的时候发现被裁剪曲面的方向不对,如图所示:
原本应该是上面的平面和下面的环拼接在一起的,但是画出来以后就错位,检查后发现数据点没问题,所以应该是裁剪中的错误,很急,恳请会的大神帮助一下,谢谢了!!
下面是我的绘制程序:
void COpenGLDC::DrawNurbsSurface(const CVector3D* pts,int uSize,int vSize,int uOrder,int vOrder,float* Weight,CNurbsCurve* OuterTrimCurve,vectorInerTrimCurve,float* uKnots , float* vKnots ,bool Trim ) //参数pts:曲面组),uSize:u向节点矢量个数,vSize:v向节点矢量个数
//uOrder:u向阶数,vOrder:v向阶数, Weight:曲面的权因子(一维数组)
//OuterTrimCurv:外圈裁剪曲线,InerTrimCurve:内圈裁剪曲线,uKnots:u向节点矢量
//vKnots:v向节点矢量
{
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
//glCullFace( GL_BACK );
glEnable(GL_AUTO_NORMAL);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
int iner_size = InerTrimCurve.size();
int size = uSize*vSize;
GLfloat* ctrlPts = new GLfloat[size*3];
for(int i=0; i<size; i++) {
ctrlPts[i*3] = pts[i].dx;
ctrlPts[i*3+1] = pts[i].dy;
ctrlPts[i*3+2] = pts[i].dz;
}
int a=sizeof(ctrlPts);
GLUnurbsObj *pNurbsObj = gluNewNurbsRenderer();
gluNurbsProperty(pNurbsObj,GLU_SAMPLING_TOLERANCE,100);
gluNurbsProperty(pNurbsObj,GLU_DISPLAY_MODE,(GLfloat) GLU_FILL);
gluBeginSurface(pNurbsObj);
gluNurbsSurface(pNurbsObj, //绘制曲面
vSize+vOrder, vKnots,
uSize+uOrder, uKnots,
uSize*3,
3,
ctrlPts,
vOrder,
uOrder,
GL_MAP2_VERTEX_3);
if(Trim == TRUE) //如果曲面被裁剪,绘制裁剪曲面
{
int Trim_size = OuterTrimCurve->m_Size;
GLfloat* Trim_ctrlPts = new GLfloat[Trim_size*3];
for(int i=0; i<Trim_size; i++) {
Trim_ctrlPts[i*3] = OuterTrimCurve->m_ctrlPts[i].dx;
Trim_ctrlPts[i*3+1] = OuterTrimCurve->m_ctrlPts[i].dy;
Trim_ctrlPts[i*3+2] = OuterTrimCurve->m_ctrlPts[i].dz;
}
gluBeginTrim(pNurbsObj);
gluNurbsCurve(pNurbsObj, //外圈裁剪曲线
OuterTrimCurve->m_Size+OuterTrimCurve->m_Order,
OuterTrimCurve->m_Knots,
3,
(float*) Trim_ctrlPts,
OuterTrimCurve->m_Order,
GLU_MAP1_TRIM_2);
gluEndTrim(pNurbsObj);
delete [] Trim_ctrlPts;
/*for(int j=0;j<iner_size;j++)
{
int Trim1_size = InerTrimCurve[j]->m_Size;
GLfloat* Trim1_ctrlPts = new GLfloat[Trim1_size*3];
for(int i=0; i<Trim1_size; i++) {
Trim1_ctrlPts[i*3] = InerTrimCurve[j]->m_ctrlPts[i].dx;
Trim1_ctrlPts[i*3+1] = InerTrimCurve[j]->m_ctrlPts[i].dy;
Trim1_ctrlPts[i*3+2] = InerTrimCurve[j]->m_ctrlPts[i].dz;
}
gluBeginTrim(pNurbsObj);
//gluNurbsProperty(pNurbsObj,GLU_SAMPLING_TOLERANCE,25.0);
gluBeginCurve(pNurbsObj);
gluNurbsCurve(pNurbsObj,
InerTrimCurve[j]->m_Size+InerTrimCurve[j]->m_Order,
InerTrimCurve[j]->m_Knots,
3,
(float*) Trim1_ctrlPts,
InerTrimCurve[j]->m_Order,
GL_MAP1_VERTEX_3);
gluEndCurve(pNurbsObj);
gluEndTrim(pNurbsObj);
delete [] Trim1_ctrlPts;
}*/
}
gluEndSurface(pNurbsObj);
if(pNurbsObj)
gluDeleteNurbsRenderer(pNurbsObj);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_FALSE);
glCullFace(GL_BACK|GL_FRONT);
glEnable(GL_CULL_FACE);
glFlush();
delete [] ctrlPts;
}
要画出一个曲面,首先要有曲面的表达方式。对于OpenGL渲染而言,个人觉得曲面可以分“参数曲面”跟“非参数曲面”两种。 参数曲面的通常由一组控制点(称为Patch,可以理解为一个很粗糙的曲面)定义,用一个正方形或者单位等边三角形作为参数空间(称为Domain),在Domain上通过一定精细度采样多个参数点,最后把这些Domain上的采样点映射到Patch所在空间上的三维点坐标,构成比Patch更加精细的曲面。
非参数化的曲面不像参数曲面那样直接算出曲面点,它是通过某种细分算法逐步把Patch细分的,例如Catmull-Clark【1】曲面是通过递归细分得到的,这种方法没办法通过GPU硬件直接支持,但可以通过参数曲面近似模拟之【2】。
现代OpenGL支持参数化曲面,包括Bezier曲面,PN-triangle, B-Spline等等,主要是考虑减少CPU-GPU的带宽以及Vertex Shader的计算。理论上应用可以在pipeline外边用software自行根据细分规则,通过参数也好,通过递归也好,生成光滑精细的Mesh,然后把曲面上的三角形逐个送入GPU去rendering,但这种方法明显性能不好。 新的OpenGL引入Tessellation Shader来渲染曲面,包含三大块:Control Shader, Primitive Generator,Evaluation Shader。Tessellation Shader的输入仅仅是Control point而非Mesh。
(1)Control Shader是可编程的,负责对patch做变换,比如我们旋转patch,那么后面生成的整个精细曲面会被旋转,作用:便于更轻量地实现动画
在Control Shader可以改变控制点的数量,做任意自由的变换。如有必要,它还可以计算一些整个Patch共享的常量(Patch Constant)给Evaluation Shader使用,这个就不展开了。
(2)Primitive Generator是个不可编程但可配置的模块,负责在一个Domain上按应用指定的精细程度采样Domain点,在Domain空间上实现了细分,最终要Evaluate Shader将Domain坐标变成三维坐标。
(3)Evaluation Shader也是可编程模块,Control Shader会把转化后的新Patch送给Evaluation Shader,Primitive Generator所有采样点送给Evaluation Shader,然后Evaluation Shader根据应用写的Shader(evalute公式)把每个采样点转化为三维曲面三维坐标点,众多的曲面点就能构成了更精细的曲面了。