OpenGL学习笔记2:绘制和混合

绘制

OpenGL几何图元

在使用OpenGL在计算机屏幕上进行绘图时不需要从一个点的绘制开始。我们关心的不是物理屏幕坐标和像素,而是视景体中的位置坐标,我们将从7个由定义的几何图元来开始绘制实心几何图形:

图元描述
GL_POINTS每个顶点在屏幕上都是一个单独的点
GL_LINES每一对顶点定义了一个线段
GL_LINE_STRIP一个从第一个顶点依次经过每个后续顶点而绘制的线条
GL_LINE_LOOP和GL_LINE_STRIP相同,但最后一个顶点和第一个顶点连接了起来
GL_TRIANGLES每3个顶点定义了一个新的三角形
GL_TRIANGLE_STRIP共用一个条带(strip)上的顶点的一组三角形
GL_TRIANGLE_FAN以一个圆点为中心呈扇形排列,共用相邻顶点的一组三角形


我们可以通过glPointSize改变默认点的大小
void glPointSize(GLfloat size);
使用下面的函数可以获得点大小的范围,以及他们之间的最小间隔。
GLfloat sizes[2]; //存储支持的点大小范围
GLfloat step; //存储支持的点大小增量
//获取支持的点大小范围和步长(增量)
glGetFloatv(GL_POINT_SIZE_RANGE,sizes);
glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step);
还可以通过使用程序点大小模式来设置点大小。
glEnable(GL_PROGRAM_POINT_SIZE);
线
默认情况下,线段的宽度为一个像素。改变线段宽度的唯一方式是使用函数glLineWidth。
void glLineWidth(GLfloat width);
面(三角形)
三角形的绘制按三个顶点的顺序进行绘制,根据三个顶点的方向不同分为顺时针环绕和逆时针环绕:OpenGL认为具有逆时针方向环绕的多边形是正面的。
如果想改变OpenGL的这个默认行为,可以调用下面这个函数:
glFrontFace(GL_CW);
GL_CW参数告诉OpenGL顺时针环绕的多边形将被认为是正面的。

Gbatch批次容器

GBatch可以作为7种几何图元简单批次容器使用,而且它知道在使用GLShaderManager支持的任意存储着色器时如何对图元进行渲染:
首先:对批次进行初始化,告诉这个类它代表哪种图元,其中包括的顶点数,以及一组或两组纹理坐标。
void GLBatch::Begin(GLenum primitive,Gluint nVerts,GLuint nTextureUnits=0);
然后:至少要赋值一个由3个分量(x,y,z)顶点组成的数组
void GLBatch::CopyVertexData3f(GLfloat *vVerts);
我们还可以选择赋值表面法线、颜色和纹理坐标。
void GLBatch::CopyNormalDataf(GLfloat *vVerts);
void GLBatch::CopyColorData4f(GLfloat *vColors);
void GLBatch::CopyTexCoordData2f(GLfloat *vTexCoords,GLuint uiTextureLayer);
最后:我们可以调用End来表明已经完成了数据复制工作,并且将设置内部标记以通知这个类包含哪些属性。
void GLBatch::End(void);

关闭不必要的渲染

正面和背面剔除
面的按顺时针逆时针来区分正反面这一机制实际上是为了选择性的剔除正反面(比如你看不到背面就不需要渲染)。
开启表面剔除:
glEnable(GL_CULL_FACE);
关闭表面剔除:
glDisable(GL_CULL_FACE);
指明剔除的是正面还是背面:
void glCullFace(GLenum mode);
mode参数的可用值为GL_FRONT/GL_BACK或GL_FRONT_AND_BACK。

深度测试

深度测试将消除哪些应该被已存在像素覆盖的像素,这将节省客观的存储器带宽。
申请颜色缓冲区和深度缓冲区:
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH);
启用深度测试:
glEnable(GL_DEPTH_TEST);
多边形模式
函数glPolygonMode允许将多边形渲染成实体、轮廓或只有点。此外,我们可以在多边形两面都应用这个渲染模式,也可以只在正面或背面应用。
void glPolygonMode(GLenum face,GLenum mode);
和表面剔除一样,face参数的可用值为GL_FRONT、GL_BACK或GL_FRONT_AND_BACK。而mode参数的可用值为GL_FILL(默认值)、GL_LINE或GL_POINT。

多边形偏移

在右移要将两个和图形绘制到同一位置时,有可能造成意想不到的后果,多边形偏移可以让我们避免这些后果。
glPolygonOffset函数使我们可以调节片段的深度值,这样就能使深度值产生偏移而并不实际改变3D空间中的物理位置。
void glPolygonOffset(GLfloat factor,Glfloat units);
应用到片段上的总偏移可以通过下面的方程式表示:
Depth Offset=(DZ*factor)+(r*units)
其中DZ是深度值(z值)相对于多边形屏幕区域的变化量,而r则是使深度缓冲区值产生变化的最小值。负值将使z离我们更近,正值将他们移动到更远。

裁剪

裁剪可以只刷新屏幕上指定的一个矩形区域。
开始裁剪测试
glEnable(GL_SCISSOR_TEST);
结束裁剪测试
glDisable(GL_SCISSOR_TEST)
指定矩形窗口坐标
void glScissor(Glint x,Glint y,GLsizei width,GLsizei height);

混合

打开混合功能
glEnable(GL_BLEND);
关闭混合功能
glDisable(GL_BLEND);

组合颜色

已经存在颜色缓冲区中的颜色值叫做目标颜色,作为当前渲染命令的结果进入颜色缓冲区的颜色值称为源颜色。
默认情况下,混合方程式如下所示:
Cf=(Cs*S)+(Cd*D)
其中,Cf是最终计算产生的颜色,Cs是源颜色,Cd则是目标颜色,S和D分别是源和目标混合因子。这些混合因子使用下面这个函数进行设置的:
glBlendFunc(GLenum S,GLenum D);
S和D都是枚举值:
OpenGL混合因子

函数 RGB混合因子Alpha混合因子
GL_ZERO(0,0,0) 0
GL_ONE(1,1,1) 1
GL_SRC_COLOR(Rs,Gs,Bs) As
GL_ONE_MINUS_SRC_COLOR(1,1,1)-(Rs,Gs,Bs) 1-As
GL_DST_COLOR(Rd,Gd,Bd) Ad
GL_ONE_MINUS_DST_COLOR(1,1,1)-(Rd,Gd,Bd) 1-Ad
GL_SRC_ALPHA(As,As,As) As
GL_ONE_MINUS_SRC_ALPHA(1,1,1)-(As,As,As) 1-As
GL__DST_ALPHA(Ad,Ad,Ad) Ad
GL_ONE_MINUS_DST_ALPHA(1,1,1)-(Ad,Ad,Ad) 1-Ad
GL_CONSTANT_COLOR(Rc,Gc,Bc) Ac
GL_ONE_MINUS_CONSTANT_COLOR(1,1,1)-(Rc,Gc,Bc) 1-Ac
GL_CONSTANT_ALPHA(Ac,Ac,Ac) Ac
GL_ONE_MINUS_CONSTANT_ALPHA(1,1,1)-(Ac,Ac,Ac) 1-Ac
GL_SRC_ALPHA_SATURATE(f,f,f)* 1

*其中f=min(As,1-Ad)
改变混合方程式
我们可以通过下面的函数进行混合方程式的选择:
void glBlendEquation(GLenum mode);
其中mode有下列可选项

模式函数
GL_FUNC_ADDCf=(Cs*S)+(Cd*D)
GL_FUNC_SUBTRACTCf=(Cs*S)-(Cd*D)
GL_FUNC_REVERSE_SUBTRACTCf=(Cd*D)-(Cs*S)
GL_MINCf=min(Cs,Cd)
GL_MAXCf=max(Cs,Cd)

除了glBlendFunc之外,我们还可以利用下面的函数更加灵活地进行选择:
void glBlendFuncSeqarate(GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLenum dstAlpha);
可以用下面这个函数修改常量混合颜色:
void glBlendColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha);

抗锯齿

首先:我们必须启用混合功能,并像实现透明一样设置混合函数:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
然后我们需要确保把混合方程式设置为GL_ADD,这是默认的设置。
之后再调用glEnable函数对点、直线和(或)多边形(任何实心图元)进行抗锯齿处理:
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);

多重采样

当无法使用抗锯齿或者处于性能考虑不想使用抗锯齿又想要平滑效果时可以使用多重采样技术。
为了进行多重采样,首先必须获得一个支持多重采样帧缓冲区的渲染环境。这在不同平台中可能各不相同。但是GLUT提供了一个尾端(GLUTMULTISAMPLE),允许请求这种帧缓冲区。例如,为了请求一个多重采样,完全颜色、带深度的双缓冲区,可以调用:
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH|GLUT_MULTISAMPLE);
可以使用glEnable/glDisable组合(使用GL_MULTISAMPLE标记)打开或关闭多重采样。
多重采样需要注意的一个地方是当它被启用时,点,直线和多边形的平滑特性都将被忽略。
多重采样缓冲区在默认情况下使用片段的RGB值,并不包括颜色的alpha成分。我们可以通过调用glEnable来修改这个行为:
GL_SAMPLE_ALPHA_TO_COVERAGE——使用alpha值
GL_SAMPLE_ALPHA_TO_ON将alpha值设为1并使用它
GL_SAMPLE_COVERAGE——使用glSampleCoverage所设置的值
当启用GL_SAMPLE_COVERAGE时,glSampleConverage函数允许指定一个特定的值,它是与片段覆盖值进行按位与操作的结果。
void glSampleConverage(GLclampf value,GLboolean invert);

转载请注明出处:http://blog.csdn.net/ylbs110/article/details/51706823

示例

#include "stdafx.h"
#include <GLTools.h>
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLGeometryTransform.h>

#include <math.h>
#define GLUT_DISABLE_ATEXIT_HACK
#include <GLUT.H>


/*
* 当libjpeg-turbo为vs2010编译时,vs2015下静态链接libjpeg-turbo会链接出错:找不到__iob_func,
* 增加__iob_func到__acrt_iob_func的转换函数解决此问题,
* 当libjpeg-turbo用vs2015编译时,不需要此补丁文件
*/
#if _MSC_VER>=1900
#include "stdio.h" 
_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#ifdef __cplusplus 
extern "C"
#endif 
FILE* __cdecl __iob_func(unsigned i) {
    return __acrt_iob_func(i);
}
#endif /* _MSC_VER>=1900 */



/////////////////////////////////////////////////////////////////////////////////
GLShaderManager     shaderManager;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLFrame             cameraFrame;
GLFrame             objectFrame;
GLFrustum           viewFrustum;

GLBatch             Batch1;
GLBatch             Batch2;

GLGeometryTransform transformPipeline;

GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 0.5f };
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };

void SetupRC()
{
    //背景色
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f);

    shaderManager.InitializeStockShaders();

    glEnable(GL_DEPTH_TEST);

    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

    cameraFrame.MoveForward(-15.0f);

    //////////////////////////////////////////////////////////////////////
    // 构成图形的坐标数组
    GLfloat vCoast1[100][3];
    GLfloat angle = 0;
    for (int i = 0; i < 100; i++){
        angle += M3D_2PI / 6.0f;
        vCoast1[i][0] = float(-5+i*0.2);
        vCoast1[i][1] = float(sin(float(angle)));
        vCoast1[i][2] = float(cos(float(angle)));
    }

    // 将坐标数组拷贝到batch中
    Batch1.Begin(GL_TRIANGLE_STRIP, 100);
    Batch1.CopyVertexData3f(vCoast1);
    Batch1.End();

    GLfloat vCoast2[4][3] = {
        2.5f, 2.5f, -2.0f,
        2.5f, -2.5f, -2.0f,
        -2.5f, 2.5f, -2.0f,
        -2.5f, -2.5f, -2.0f
    };

    Batch2.Begin(GL_TRIANGLE_STRIP, 4);
    Batch2.CopyVertexData3f(vCoast2);
    Batch2.End();
}

/////////////////////////////////////////////////////////////////////////
void DrawWireFramedBatch(GLBatch* pBatch)
{
    // 平面着色器将多边形填充为绿色
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();

    // 多边形偏移
    glPolygonOffset(-1.0f, -1.0f);
    glEnable(GL_POLYGON_OFFSET_LINE);

    // 对线设置抗锯齿
    glEnable(GL_LINE_SMOOTH);
    //启用抗锯齿的同时要启用混合功能
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // 设置线框的显式模式为正反面同时显式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glLineWidth(2.5f);
    // 平面着色器将多边形线框填充为黑色
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();

    // 重置所有设置
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
}

void RenderScene(void)
{
     //Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    //书上例子里的,目前不明白,后面应该会学到
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.MultMatrix(mCamera);

    M3DMatrix44f mObjectFrame;
    objectFrame.GetMatrix(mObjectFrame);
    modelViewMatrix.MultMatrix(mObjectFrame);


    // 平面着色器将多边形填充为绿色
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    Batch1.Draw();

    // 多边形偏移
    glPolygonOffset(-1.0f, -1.0f);
    glEnable(GL_POLYGON_OFFSET_LINE);

    // 对线设置抗锯齿
    glEnable(GL_LINE_SMOOTH);
    //启用抗锯齿的同时要启用混合功能
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // 设置线框的显式模式为正反面同时显式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glLineWidth(2.5f);
    // 平面着色器将多边形线框填充为黑色
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    Batch1.Draw();

    // 重置所有设置
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);



    // 平面着色器将多边形填充为红色
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
    Batch2.Draw();
    glDisable(GL_POLYGON_OFFSET_LINE);
    glDisable(GL_BLEND);


    modelViewMatrix.PopMatrix();


    //开启裁剪测试
    glEnable(GL_SCISSOR_TEST);
    //设定裁剪区域
    glScissor(200, 200, 200, 200);
    //设定裁剪颜色
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    //关闭裁剪区域
    glDisable(GL_SCISSOR_TEST);



    // Flush drawing commands
    glutSwapBuffers();
}

void SpecialKeys(int key, int x, int y)
{
    if (key == GLUT_KEY_UP)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);

    if (key == GLUT_KEY_DOWN)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);

    if (key == GLUT_KEY_LEFT)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);

    if (key == GLUT_KEY_RIGHT)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);

    glutPostRedisplay();
}

///////////////////////////////////////////////////////////////////////////////
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
    glViewport(0, 0, w, h);
    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    modelViewMatrix.LoadIdentity();
}
int _tmain(int argc, char* argv[])
{

    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(800, 600);
    glutCreateWindow("GL_POINTS");
    glutReshapeFunc(ChangeSize);
    glutSpecialFunc(SpecialKeys);
    glutDisplayFunc(RenderScene);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    SetupRC();
    glutMainLoop();

    return 0;
}

运行结果
这里写图片描述
按方向键可以转动画面,其中经过裁剪的部分并没有跟着转动,但是转动后整个画面颜色都被蓝色渲染了一层,原因不明,暂且搁下学习更多内容后再回来看。
这里写图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值