zoukankan      html  css  js  c++  java
  • opengl 反走样 混合 多重采样 blend multisample

    1. 反走样

            在光栅图形显示器上绘制非水平且非垂直的直线或多边形边界时,或多或少会呈现锯齿状或台阶状外观。这是因为直线、多边形、色彩边界等是连续的,而光栅则是由离散的点组成,在光栅显示设备上表现直线、多边形等,必须在离散位置采样。由于采样不充分重建后造成的信息失真,就叫走样(aliasing)。而用于减少或消除这种效果的技术,就称为反走样(antialiasing)。

    2. OpenGL反走样的实现

    OpengL中的反走样采用的是融合的技术,来实现点、线和图形的边沿以及雾和颜色和纹理的插值运算。OpenGL实现反走样需要满足两个条件,一是启用混合,二是启用针对几何图元的反走样处理。
    3. OpenGL混合Blend
    混合是什么呢?混合就是把两种颜色混在一起。具体一点,就是把某一像素位置原来的颜色和将要画上去的颜色,通过某种方式混在一起,从而实现特殊的效果。
    假设我们需要绘制这样一个场景:透过红色的玻璃去看绿色的物体,那么可以先绘制绿色的物体,再绘制红色玻璃。在绘制红色玻璃的时候,利用“混合”功能,把将要绘制上去的红色和原来的绿色进行混合,于是得到一种新的颜色,看上去就好像玻璃是半透明的。

    要使用OpenGL的混合功能,只需要调用:glEnable(GL_BLEND);即可。
    要关闭OpenGL的混合功能,只需要调用:glDisable(GL_BLEND);即可。
    glBlendFunc(源因子, 目标因子)完成混合方法的定义。
    举例来说:
    如果设置了glBlendFunc(GL_ONE, GL_ZERO);,则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
    如果设置了glBlendFunc(GL_ZERO, GL_ONE);,则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
    如果设置了glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
    如果设置了glBlendFunc(GL_ONE, GL_ONE);,则表示完全使用源颜色和目标颜色,最终的颜色实际上就是两种颜色的简单相加。例如红色(1, 0, 0)和绿色(0, 1, 0)相加得到(1, 1, 0),结果为黄色。
    注意:
    所谓源颜色和目标颜色,是跟绘制的顺序有关的。假如先绘制了一个红色的物体,再在其上绘制绿色的物体。则绿色是源颜色,红色是目标颜色。如果顺序反过来,则红色就是源颜色,绿色才是目标颜色。在绘制时,应该注意顺序,使得绘制的源颜色与设置的源因子对应,目标颜色与设置的目标因子对应。不要被混乱的顺序搞晕了。
     
    三维混合的注意事项
     问题
    :也许你迫不及待的想要绘制一个三维的带有半透明物体的场景了。但是现在恐怕还不行,还有一点是在进行三维场景的混合时必须注意的,那就是深度缓冲。深度缓冲是这样一段数据,它记录了每一个像素距离观察者有多近。在启用深度缓冲测试的情况下,如果将要绘制的像素比原来的像素更近,则像素将被绘制。否则,像素就会被忽略掉,不进行绘制。这在绘制不透明的物体时非常有用——不管是先绘制近的物体再绘制远的物体,还是先绘制远的物体再绘制近的物体,或者干脆以混乱的顺序进行绘制,最后的显示结果总是近的物体遮住远的物体。然而在你需要实现半透明效果时,发现一切都不是那么美好了。如果你绘制了一个近距离的半透明物体,则它在深度缓冲区内保留了一些信息,使得远处的物体将无法再被绘制出来。虽然半透明的物体仍然半透明,但透过它看到的却不是正确的内容了。
    方案:要解决以上问题,需要在绘制半透明物体时将深度缓冲区设置为只读,这样一来,虽然半透明物体被绘制上去了,深度缓冲区还保持在原来的状态。如果再有一个物体出现在半透明物体之后,在不透明物体之前,则它也可以被绘制(因为此时深度缓冲区中记录的是那个不透明物体的深度)。以后再要绘制不透明物体时,只需要再将深度缓冲区设置为可读可写的形式即可。嗯?你问我怎么绘制一个一部分半透明一部分不透明的物体?这个好办,只需要把物体分为两个部分,一部分全是半透明 的,一部分全是不透明的,分别绘制就可以了。
    注意: 即使使用了以上技巧,我们仍然不能随心所欲的按照混乱顺序来进行绘制。必须是先绘制不透明的物体,然后绘制透明的物体。否则,假设背景为蓝色,近处一块红色玻璃,中间一个绿色物体。如果先绘制红色半透明玻璃的话,它先和蓝色背景进行混合,则以后绘制中间 的绿色物体时,想单独与红色玻璃混合已经不能实现了。
    总结:绘制顺序就是首先绘制所有不透明的物体。如果两个物体都是不透明的,则谁先谁后都没有关系。然后,将深度缓冲区设置为只读。接下来,绘制所有半透明的物体。如果两个物体都是半透明的,则谁先谁后只需要根据自己的意愿(注意了,先绘制的将成为“目标颜色”,后绘制的将成为“源颜色”,所以绘制的顺序将会对结果造成一些影响)。最后,将深度缓冲区设置为可读可写形式。
    调用glDepthMask(GL_FALSE);可将深度缓冲区设置为只读形式。调用glDepthMask(GL_TRUE);可将深度缓冲区设置为可读可写形式。
    4.OpenGL的反走样函数

    void glHint(GLenum target,GLenum hint); 

             hint定义了反走样的方法
              GL_FASTEST 给出最有效的选择 
              GL_NICEST 给出最高质量的选择 
              GL_DONT_CARE 没有选择

              target定义反走样的对象

             GL_POINT_SMOOTH_HINT 指定点

                GL_LINE_SMOOTH_HINT 线
                GL_POLYGON_SMOOTH_HINT 多边形的采样质量 
                GL_FOG_HINT 指出雾化计算是按每个象素进行(GL_NICEST),还是按每个顶点进行(GL_FASTEST) 
                GL_PERSPECTIVE_CORRECTION_HINT 指定颜色纹理插值的质量 
                其中GL_PERSPECTIVE_CORRECTION_HINT用以纠正单纯线性插值带来的观察错误。

    5.OpenGL的反走样实例1---点的反走样
    #include<windows.h>
    #include<gl/glut.h>
    #include<math.h>
    #define x_Screen 800
    #define y_Screen    600
    #define little 50
    #define middle 20
    #define large   8
    void myBackground()
    {
    glClearColor(0.0,0.0,0.0,1.0);
    glColor3f(1.0,1.0,1.0);
    }
     
    void myDisplay()
    {
    glEnable ( GL_DEPTH_TEST );
    //如果没有抗锯齿,则点为方形的。如果我们启动抗锯齿设置,则点是一个圆点。
    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); // Make round points, not square points
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);  // Antialias the lines
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    int i;
    glBegin(GL_POINTS);
    for(i=0;i<little;i++)
    glVertex2f(50.0+rand()%x_Screen,50.0+rand()%y_Screen);
    glEnd();
    glPointSize(2);
    glBegin(GL_POINTS);
    for(i=0;i<middle;i++)
    glVertex2f(50.0+rand()%x_Screen,50.0+rand()%y_Screen);
    glEnd();
     
    glPointSize(8);
    glBegin(GL_POINTS);
    for(i=0;i<large;i++)
    glVertex2f(50.0+rand()%x_Screen,50.0+rand()%y_Screen);
    glEnd();
     
    glBegin(GL_POLYGON);
    for(i=0;i<64;i++)
    glVertex2f(600+50.0*cos((float)i/10) ,500+50.0*sin((float)i/10) );
    glEnd();
    glLineWidth(3);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glEnable(GL_LINE_SMOOTH);
    glBegin(GL_LINE_STRIP);
    for(i=0;i<19;i++){
    glVertex2f(rand()%10+i*70,rand()%50+50.0+(i%2)*80);
     
    }
    glEnd();
     
    glutSwapBuffers();
    }
    void myChange(int w,int h)
    glViewport(0,0,w,h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
     
    gluOrtho2D(0.0,x_Screen,0.0,y_Screen);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }
     
     
    void main()
    {
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
    glutInitWindowSize(x_Screen,y_Screen);
    glutCreateWindow("Star");
    glutDisplayFunc(myDisplay);
    glutReshapeFunc(myChange);
    myBackground();
    glutMainLoop();
     
    }
    6.OpenGL的反走样实例2---线的反走样
    #include <GL/glut.h>
    #include <stdio.h>
     
    static float rotAngle = 0.;
     
    void init(void)
    {
    GLfloat values[2];
    glGetFloatv(GL_LINE_WIDTH_GRANULARITY, values);
    printf("GL_LINE_WIDTH_GRANULARITY value is %3.1f ", values[0]);
     
    glGetFloatv(GL_LINE_WIDTH_RANGE, values);
    printf("GL_LINE_WIDTH_RANGE values are %3.1f %3.1f ", values[0], values[1]);
     
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
     
    glLineWidth(1.5);
     
    glClearColor(0.0, 0.0, 0.0, 0.0);
    }
     
    void display(void)
    {
       glClear(GL_COLOR_BUFFER_BIT);
     
       glColor3f(0.0, 1.0, 0.0);
       glPushMatrix();
       glRotatef(-rotAngle, 0.0, 0.0, 0.1);
       glBegin(GL_LINES);
       glVertex2f(-0.5, 0.5);
       glVertex2f(0.5, -0.5);
       glEnd();
       glPopMatrix();
       
       glColor3f(0.0, 0.0, 1.0);
       glPushMatrix();
       glRotatef(rotAngle, 0.0, 0.0, 0.1);
       glBegin(GL_LINES);
       glVertex2f(0.5, 0.5);
       glVertex2f(-0.5, -0.5);
       glEnd();
       glPopMatrix();
       
       glFlush();
    }
     
    void reshape(int w, int h)
    {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h) 
    gluOrtho2D (-1.0, 1.0, -1.0*(GLfloat)h/(GLfloat)w, 1.0*(GLfloat)h/(GLfloat)w);
    else 
    gluOrtho2D (-1.0*(GLfloat)w/(GLfloat)h, 1.0*(GLfloat)w/(GLfloat)h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }
     
    void keyboard(unsigned char key, int x, int y)
    {
    switch (key) 
    {
    case 'r': case 'R':
    rotAngle += 20.;
    if (rotAngle >= 360.0) rotAngle = 0.0;
    glutPostRedisplay(); 
    break;
    case 27:
    exit(0);
    break;
    default:
    break;
    }
    }
     
    int main(int argc, char** argv)
    {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(200, 200);
    glutCreateWindow(argv[0]);
    init();
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
    }

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------

    1.       使用颜色混合来消除一些锯齿, 主要针对点和线以及不相互重叠的多边形的反锯齿。

    反锯齿设置代码如下:

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glEnable(GL_BLEND);

    glEnable(GL_POINT_SMOOTH);

    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);

    glEnable(GL_LINE_SMOOTH);

    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

    glEnable(GL_POLYGON_SMOOTH);

    glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

    取消反锯齿代码如下:

    glDisable(GL_BLEND);

    glDisable(GL_LINE_SMOOTH);

    glDisable(GL_POINT_SMOOTH);

    glDisable(GL_POLYGON_SMOOTH);

    2.       多重采样(Multisample)

    并不是所有的平台都支持通过颜色混合来消除多边形锯齿,此外,多边形混合还有顺序的问题,使用起来不方便。OpenGL引入了多重采样来解决多边形锯齿的问题,并增加了一个包含颜色、尝试以及模块缓存值的帧缓存。开启多重采样功能的代码如下:

    //申请一个采用了双重缓存,包含颜色,深度的帧缓存和多重采样。

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE);

       glEnable(GL_MULTISAMPLE);//开启多重缓存

       glDisable(GL_MULTISAMPLE);//关闭多重缓存

      

       注: 多重采样和混合不能同时开启, 这两种方法只能互斥使用。即使用任何一个方法前需要禁用另一个方法。

  • 相关阅读:
    利用 AlwaysInstallElevated 提权
    一批内网文章分享
    关于DLL劫持提权
    Xposed+JustTrustMe关闭ssl证书验证
    关于windows组策略首选项提权
    关于代替Procdump dump lsass的两种方法
    与ServletContext相关的监听器
    java EE 监听器
    ServletContext
    GenericServlet
  • 原文地址:https://www.cnblogs.com/ITcode/p/3944243.html
Copyright © 2011-2022 走看看