OpenGL

005 --- 多边形偏移、裁剪、混合、 抗锯齿

2020-07-13  本文已影响0人  清风烈酒2157
image.png

多边形偏移

前言

虽然深度测试能够实现真实的视觉并提高性能,但有时也会带来一些小麻.如果有意将两个几何图形绘制到同一位置时,会带来一些问题.

例如,如果绘制一架大型飞机,然后在飞机在绘制一个较小的的但却与飞机在同一物理空间的图形,这就叫做 贴花.

这种情况下星形图案的深度值将会与绘制原来的飞机的深度缓冲区值相同,或者机会相同,这会导致片段深度测试不可预料的通过或失败,这种情况称为 z-fighting(z冲突).

如何处理z冲突

另外一种情况,绘制实心的几何图形但又要突出它的边时.

image.png image.png

问题在于,如果我们在实体条带的同一位置绘制线框,就会遇到 z-fighting(z冲突) 的问题.

  1. 手动调整z值进行一点点偏移,但可能会出现图形悬浮(不推荐)
  2. 利用 多边形偏移 调节片段的深度值,但实际不改变3D空间物理位置(推荐)

GLAPI void GLAPIENTRY glPolygonOffset (GLfloat factor, GLfloat units);

image.png
  1. 其中 DZ是深度值相对多边形屏幕区域的变化量,r 是使深度缓冲区值产生变化的最小值,这2个值都是 OpenGL 内部的值,我们不用关心,我们是通过控制 factorunits 达到效果.

  2. 负值将是z值距离我们更近,而正直则会将他们移动的更远.

  3. 一般而言,只要将-1 和 -1 这样简单赋值给 glPolygonOffset基本可以满足需求.

image.png

void DrawWireFramedBatch(GLBatch* pBatch)
{
    /*------------画绿色部分----------------*/
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
          --transformPipeline 变换管线(指定了2个矩阵堆栈)
     参数3:颜色值
    */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();
    
    /*-----------边框部分-------------------*/
    /*
        glEnable(GLenum mode); 用于启用各种功能。功能由参数决定
        参数列表:http://blog.csdn.net/augusdi/article/details/23747081
        注意:glEnable() 不能写在glBegin() 和 glEnd()中间
        GL_POLYGON_OFFSET_LINE  根据函数glPolygonOffset的设置,启用线的深度偏移
        GL_LINE_SMOOTH          执行后,过虑线点的锯齿
        GL_BLEND                启用颜色混合。例如实现半透明效果
        GL_DEPTH_TEST           启用深度测试 根据坐标的远近自动隐藏被遮住的图形(材料
     
     
        glDisable(GLenum mode); 用于关闭指定的功能 功能由参数决定
     
     */
    
    //画黑色边框
    glPolygonOffset(-1.0f, -1.0f);// 偏移深度,在同一位置要绘制填充和边线,会产生z冲突,所以要偏移
    glEnable(GL_POLYGON_OFFSET_LINE);
    
    
     /*-----------画反锯齿,让黑边好看些-------------------*/
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    
    
    /*-----------绘制线框几何黑色版 三种模式,实心,边框,点,可以作用在正面,背面,或者两面------------------*/
    
    //通过调用glPolygonMode将多边形正面或者背面设为线框模式,实现线框渲染
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //设置线条宽度
    glLineWidth(2.5f);
    
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
         --transformPipeline.GetModelViewProjectionMatrix() 获取的
          GetMatrix函数就可以获得矩阵堆栈顶部的值
     参数3:颜色值(黑色)
     */
    
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();


/*-----------复原原本的设置------------------*/
    //通过调用glPolygonMode将多边形正面或者背面设为全部填充模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
    
    
}
image.png image.png

裁剪

另一种提高性能的方法是只刷新屏幕上发生变化的部分.我们还可能需要将OpenGL渲染绘制在窗口中一个较小的矩形区域中.OpenGL可以让我们在想要进行渲染的窗口中指定一个裁剪框.


glEnable(GL_SCISSOR_TEST)


GLAPI void GLAPIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);


void RenderScene(void)
{
    //设置清屏颜色为蓝色
    glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    //1.现在剪成小红色分区
    //(1)设置裁剪区颜色为红色
    glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
    //(2)设置裁剪尺寸
    glScissor(100, 100, 600, 400);
    //(3)开启裁剪测试
    glEnable(GL_SCISSOR_TEST);
    //(4)开启清屏,执行裁剪
    glClear(GL_COLOR_BUFFER_BIT);
    
    // 2.裁剪一个绿色的小矩形
    //(1).设置清屏颜色为绿色
    glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
    //(2).设置裁剪尺寸
    glScissor(200, 200, 400, 200);
    //(3).开始清屏执行裁剪
    glClear(GL_COLOR_BUFFER_BIT);
    
    //关闭裁剪测试
    glDisable(GL_SCISSOR_TEST);
    
    //强制执行缓存区
    glutSwapBuffers();
}

image.png

混合

首先了解一个概念:

OpenGL渲染时会把颜色值放在颜色缓冲区中,每个片段的深度值也是防砸深度缓冲区中的.


GLAPI void GLAPIENTRY glEnable (GLenum cap);

在打开混合功能的情况下,新的颜色会与已经存在的颜色值在颜色缓冲区进行组合.这些颜色的组合方式不同会导致很多不同的特殊颜色.

组合颜色

这个颜色值包含了一个单独的红,绿,蓝乘成分以及一个可选的alpha值.

这个颜色值包含了一个单独的红,绿,蓝成分以及一个可选的alpha值.

任何情况下只要我们忽略一个alpha值,OpenGL默认设置为1.0;

当混合功能被启用时,源颜色和目标颜色的组合方式有混合方程式控制的.


Cf = (Cs * S) + (Cd * D)

Cf最终计算产生的颜色
Cs是源颜色
Cd是目标颜色
S D分别是源和目标混合因子.

正如我们看到的那样,S和D都是枚举值,而不是可以指定的实际值.

image.png

颜色值实用浮点表示的,因此可以对它们进行加减甚至乘法都是完全合法的.


 GLAPI void GLAPIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor);

  1. 这个函数表示OpenGL接受源颜色并将这个颜色(RGB值)alpha相乘
  2. 目标颜色乘以 乘以"1-源颜色的alpha值"
  3. 最后相加
/*
Rs/Gs/Bs/As - 源颜色 RGBA 各个通道的混合因子
Rd/Gd/Bd/Ad - 目标颜色 RGBA 各个通道的混合因子
Rc/Gc/Bc/Ac - 常量颜色 RGBA 各个通道的混合因子
Cs = 源颜色 = { 0.0f, 0.0f, 1.0f, 0.6f } 
Cd = 目标颜色 = { 1.0f, 0.0f, 0.0f, 1.0f } 

As = 源颜色 alpha 值 = 0.6f
Ad = 目标颜色 alpha 值 = 1.0f

S = 源颜色混合因子 = GL_SRC_ALPHA = As = 0.6f
D = 目标颜色混合因子 = GL_ONE_MINUS_SRC_COLOR = 1.0f - As = 0.4f

Cf = 最终产生颜色 = Cs * 0.6f + Cd * 0.4f = {
   0.0f * 0.6f + 1.0f * 0.4f,
   0.0f * 0.6f + 0.0f * 0.4f,
   1.0f * 0.6f + 0.0f * 0.4f,
   0.6f * 0.6f + 1.0f * 0.4f
} = { 0.4f, 0.6f, 0.0f, 0.76f }
*/
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

等价于:
Cf = (Blue * 0.6) + (Red * 0.4)

最终的颜色是原来的红色(目标颜色)与后来的颜色(源颜色)进行缩放后的结果,源颜色的alpha的值越高,天剑源颜色的成分就越多,目标颜色所保留的成分就越少.


void RenderScene(void)
{
    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
   
    //1.开启混合
    glEnable(GL_BLEND);
    //2.开启组合函数 计算混合颜色因子
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    //定义4种颜色
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 0.5f };
     GLfloat vRed1[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
    GLfloat vBlue[] = { 0.0f, 0.0f, 1.0f, 1.0f };
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    

    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
    greenBatch.Draw();
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed1);
    redBatch.Draw();
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlue);
    blueBatch.Draw();
    

    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlack);
    blackBatch.Draw();
    
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    squareBatch.Draw();
    
    //5.关闭混合功能
    glDisable(GL_BLEND);
    
    //同步绘制命令
    glutSwapBuffers();
}

image.png

绿色是红色变淡,红色还是红色,蓝色变成紫色,黑色是红色变深.

改变混合方程式

混合方程式 :

Cf = (Cs * S) + (Cd * D)

是默认的方程式,实际上,我们可以从5个不同的混合方程式中进行选择.

image.png

除了使用glBlendFunc之外,还可以利用下面函数更加灵活地选择.

    glBlendFuncSeparate(GLenum srcRGB, GLenum destRGB, GLenum srcAlpha, GLenum destAlpha);

参数srcRGB表示颜色值的源混合因子,参数destRGB表示颜色在的目标混合因子,参数srcAlpha表示Alpha值的源混合因子,参数destAlpha表示Alpha值的目标混合因子

在使用glBlendFunc传入GL_ONE_MINUS_SRC_ALPHA这种值都会在混合方程式混合一个常量颜色值.

  1. 常量混合颜色初始值{ 0.0f,0.0f,0.0f,0.0f}
  2. 使用如下函数进行修改:

void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclam pf alpha )

抗锯齿

OpenGL 混合的另外一种用途就是抗锯齿。图形边缘会出现一些吸引眼睛的注意力而让人感觉图形不自然的像素点,称为 锯齿。我们需要尽可能的逼真,尤其在游戏、模拟和艺术创造中。为了消除图元之间的锯齿,OpenGL 利用混合功能,把像素的目标颜色和周围像素的颜色进行混合。

  1. 开启混合功能

glEnable(GL_BLEND);

  1. 设置混合方程式为 GL_ADD

这个是默认值,所以不用代码再去设置一次

  1. 设置混合方程式因子为 S = GL_SRC_ALPHA, D = GL_ONE_MINUS_SRC_ALPHA

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  1. 开启抗锯齿功能(点/线/多边形)

glEnable(GL_LINE_SMOOTH) : 线
glEnable(GL_POINT_SMOOTH): 点
glEnable(GL_POLYGON_SMOOTH): 多边形(任何实心图元)

image.png image.png

glHint

有许多算法和方法可以实现抗锯齿处理的图元.任何特定的OpenGL实现都可以选择其中的一种,或者支持两种方法.

glHint函数原型:

   ```swift

void glHint(GLenum target,GLenum mod)

```

参数说明:

  1. GL_FOG_HINT:指定雾化计算的精度。如果 OpenGL实现不能有效的支持每个像素的雾化计算,则 GL_DONT_CARE和GL_FASTEST 雾化效果中每个定点的计算。

  2. GL_LINE_SMOOTH_HINT:指定反走样线段的采样质量。如果应用较大的滤波函数,GL_NICEST 在光栅化期间可以生成更多的像素段。

  3. GL_PERSPECTIVE_CORRECTION_HINT:指定颜色和纹理坐标的差值质量。如果OpenGL不能有效的支持透视修正参数差值,那么 GL_DONT_CAREGL_FASTEST 可以执行颜色、纹理坐标的简单线性差值计算。

  4. GL_POINT_SMOOTH_HINT:指定反走样点的采样质量,如果应用较大的滤波函数,GL_NICEST 在光栅化期间可以生成更多的像素段。

  5. GL_POLYGON_SMOOTH_HINT:指定反走样多边形的采样质量,如果应用较大的滤波函数,GL_NICEST 在光栅化期间可以生成更多的像素段。

  1. GL_FASTEST:选择速度最快选项。

  2. GL_NICEST:选择最高质量选项。

  3. GL_DONT_CARE:对选项不做考虑。


glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH, GL_NICEST);
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH, GL_NICEST);
glEnable(GL_POLYGON_SMOOTH);
glHint(GL_POLYGON_SMOOTH, GL_NICEST);

image.png
上一篇下一篇

猜你喜欢

热点阅读