裁剪就是去掉窗口外不可见的部分,保留在窗口中的内同。是OpenGL的管线中必不可少的一步,裁剪算法的执行效率会直接影响整个程序的效率。
裁剪可以按照线或面进行,一般使用规则裁剪框进行裁剪,也有用不规则图形进行裁剪,常见的是使用矩形框进行裁剪。
裁剪过程的难度随裁剪区域的复杂度和被裁剪物体的形状复杂程度增加。
这里用矩形裁剪框解释常用的裁剪算法。
点的裁剪相对简单,已知矩形裁剪框的两个对角线顶点坐标A(x1,y1)、B(x2,y2),判断点P(x,y)是不是在A、B坐标范围内即可。
若:
min(x1,x2) <= x <= max(x1,x2);
min(y1,y2) <= y <= max(y1,y2);
则P点在裁剪框中,否则在裁剪框外。
这里说的直线,都是线段。线的裁剪算法有很多,常见的有:cohen-sutherland算法,中点分割裁剪算法,Liang-Barsky算法,beck算法等。
这个算法的主要思想是,用四位掩码做源空运算判断线是否在裁剪框内,如果在或全部在裁剪框外,结束。如果部分在窗口中,用线和裁剪框的交点对线段进行分割,然后分割后的继续重复判断。步骤如下:
设雹渗瞎要裁剪的线段是P0P1。从P0端点出发,找出离P0点最近的可见点。从P1端点出发,找出离P1点最近的可见点。这两个可见点的连线就是喊皮裁剪框中的要保留的部分。
找可见点的方法用二分法,先取线段的中点M点,判断P1M是否可见,如果不能定为不可见,用P1M线段再2分,重复判断。
#include <graphics.h>稿乎#include <conio.h>
int xw_min,xw_max,yw_min, yw_max
main()
{
int driver,mode
int x1,y1,x2,y2
xw_min=150yw_min=100
xw_max=500yw_max=300
driver=DETECT
initgraph(&driver,&mode,"族薯")
draw_win(3)
x1=60y1=120x2=580y2=270
clip_a_line(x1,y1,x2,y2)
getch()
closegraph()
}
clip_a_line(int x1,int y1,int x2,int y2)
{
int i,code1[4],code2[2],done,display
float m
int x11,x22,y11,y22,mark
done=0
display=0
while(done==0)
{
x11=x1x22=x2y11=y1y22=y2
encode(x1,y1,code1)
encode(x2,y2,code2)
if(accept(code1,code2))
{
done=1
display=1
break
}
else if(reject(code1,code2))
{
done=1
break
}
mark=swap_if_needed(code1,code2)
if(mark==1)
{
x1=x22
x2=x11
y1=y22
y2=y11
}
if(x2==x1) m=-1
else m=(float)(y2-y1)/兆敬者(float)(x2-x1)
if(code1[0])
{
x1+=(yw_min-y1)/m
y1=yw_min
}
else if(code1[1])
{
x1-=(y1-yw_max)/m
y1=yw_max
}
else if(code1[2])
{
y1-=(x1-xw_min)*m
x1=xw_min
}
else if(code1[3])
{
y1+=(xw_max-x1)*m
x1=xw_max
}
}
if(display==1) line(x1,480-y1,x2,480-y2)
}
encode(int x,int y,int code[])
{
int i
for(i=0i<4i++) code[i]=0
if(x<xw_min)
code[2]=1
else if(x>xw_max)
code[3]=1
if(y>yw_max)
code[1]=1
else if(y<yw_min)
code[0]=1
}
accept(int code1[],int code2[])
{
int i,flag
flag=1
for(i=0i<4i++)
if((code1[i]==1)||(code2[i]==1))
{
flag=0
break
}
return(flag)
}
reject(int code1[],int code2[])
{
int i,flag
flag=0
for(i=0i<4i++)
if((code1[i]==1)&&(code2[i]==1))
{
flag=1
break
}
return(flag)
}
swap_if_needed(int code1[],int code2[])
{
int i,flag1,flag2,tmp
flag1=1
for(i=0i<4i++)
if(code1[i]==1)
{
flag1=0
break
}
flag2=1
for(i=0i<4i++)
if(code2[i]==1)
{
flag2=0
break
}
if((flag1==0)&&(flag2==0)) return(0)
if((flag1==1)&&(flag2==0))
{
for(i=0i<4i++)
{
tmp=code1[i]
code1[i]=code2[i]
code2[i]=tmp
}
return(1)
}
return(0)
}
draw_win(int c)
{
setcolor(c)
line(xw_min,480-yw_min,xw_max,480-yw_min)
line(xw_max,480-yw_min,xw_max,480-yw_max)
line(xw_max,480-yw_max,xw_min,480-yw_max)
line(xw_min,480-yw_max,xw_min,480-yw_min)
setcolor(7)
}
#include <Windows.h>#include <gl/glut.h>
//////////////////////////////////////////////////////////////////////////
//区域码
const GLint leftBitCode=0x1
const GLint rightBitCode=0x2
const GLint buttonBitCode=0x4
const GLint topBitCode=0x8
GLint winWidth=640,winHeight=480
class screenPT
{
public:
GLfloat x,y
}
inline GLint inside(GLint code){return GLint(!code)} //判断点是否在裁剪区内
inline GLint reject(GLint code1,GLint code2){return GLint(code1&code2)}//判断能否完全排除一条线段
inline GLint accept(GLint code1,GLint code2){return GLint(!(code1 | code2))} //判断能否完全接受一条线段
inline void swapPT(screenPT&a,screenPT&b){screenPT t=aa=bb=t} //交换两个点
inline void swapCode(GLubyte&a,GLubyte&b){GLubyte t=aa=bb=t} //交换两个区域码
//确定一个点所在位置的区域码
GLubyte encode(const screenPT&p,const screenPT&winMin,const screenPT&winMax)
{
GLubyte code=0x00
if(p.x<winMin.x)
code |= leftBitCode
if(p.x>winMax.x)
code |= rightBitCode
if(p.y<winMin.y)
code |= buttonBitCode
if(p.y>winMax.y)
code |= topBitCode
return code
}
/大袜兆/在屏幕上画一条未裁剪的线,由裁剪函数调好敬用
void drawOneLine(const screenPT&a,const screenPT&b)
{
glBegin(GL_LINES)
glVertex2f(a.x,a.y)
glVertex2f(b.x,b.y)
glEnd()
}
//裁剪函数
void lineClip(screenPT winMin,screenPT winMax,screenPT lineBegin,screenPT lineEnd)
{
GLubyte code1,code2 //保存两个端点的区域码
GLboolean done=false,plotLine=false //判断裁剪是否结束和是否要绘制直线
GLfloat k //滚租斜率
while(!done)
{
code1 = encode(lineBegin,winMin,winMax)
code2 = encode(lineEnd,winMin,winMax)
if(accept(code1,code2)) //当前直线能完全绘制
{
done=true
plotLine=true
}
else
{
if(reject(code1,code2)) //当前直线能完全排除
done = true
else
{
if(inside(code1)) //若lineBegin端点在裁剪区内则交换两个端点使它在裁剪区外
{
swapPT(lineBegin,lineEnd)
swapCode(code1,code2)
}
//计算斜率
if(lineBegin.x != lineEnd.x)
k = (lineEnd.y-lineBegin.y)/(lineEnd.x-lineBegin.x)
//开始裁剪,以下与运算若结果为真,
//则lineBegin在边界外,此时将lineBegin移向直线与该边界的交点
if(code1 &leftBitCode)
{
lineBegin.y += (winMin.x-lineBegin.x)*k
lineBegin.x = winMin.x
}
else if(code1 &rightBitCode)
{
lineBegin.y += (winMax.x-lineBegin.x)*k
lineBegin.x = winMax.x
}
else if(code1 &buttonBitCode)
{
if(lineBegin.x != lineEnd.x)
lineBegin.x += (winMin.y-lineBegin.y)/k
lineBegin.y = winMin.y
}
else if(code1 &topBitCode)
{
if(lineBegin.x != lineEnd.x)
lineBegin.x += (winMax.y-lineBegin.y)/k
lineBegin.y = winMax.y
}
}
}
}
if(plotLine)
drawOneLine(lineBegin,lineEnd)//绘制裁剪好的直线
}
//////////////////////////////////////////////////////////////////////////
void rect(screenPT winMin,screenPT winMax)
{
glBegin(GL_LINE_LOOP)
glVertex2f(winMin.x,winMin.y)
glVertex2f(winMax.x,winMin.y)
glVertex2f(winMax.x,winMax.y)
glVertex2f(winMin.x,winMax.y)
glEnd()
}
void init()
{
glViewport(0,0,winWidth,winHeight)
glClearColor(1.0,1.0,1.0,0.0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0,winWidth,0,winHeight)
glMatrixMode(GL_MODELVIEW)
}
void display()
{
screenPT winMin,winMax,lineBegin,lineEnd
winMin.x=100.0winMin.y=50.0
winMax.x=400.0winMax.y=300.0
lineBegin.x=0.0 lineBegin.y=0.0
lineEnd.x=winWidthlineEnd.y=winHeight
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(0.0,0.0,0.0)
rect(winMin,winMax) //为裁剪区域绘制一个边框
lineClip(winMin,winMax,lineBegin,lineEnd)
lineBegin.y=240.0 lineEnd.y=240.0
lineClip(winMin,winMax,lineBegin,lineEnd)
lineBegin.x=320.0 lineBegin.y=0.0
lineEnd.x=320.0 lineEnd.y=winHeight
lineClip(winMin,winMax,lineBegin,lineEnd)
glFlush()
}
int main(int argc,char** argv)
{
glutInit(&argc,argv)
glutInitWindowPosition(100,100)
glutInitWindowSize(winWidth,winHeight)
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutCreateWindow("my app")
init()
glutDisplayFunc(display)
glutMainLoop()
return 0
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)