OpenGL超级宝典学习笔记——纹理高级(三)


纹理组合器

OpenGL的纹理组合器可以控制多重纹理的片段是如何组合的。一般情况下,我们可以简单的为每个纹理单元设置一个纹理环境模式(GL_REPLACE,GL_DECAL,GL_ADD和GL_MODULATE),把每个纹理应用的结果添加到下一个纹理单元中。然而纹理组合器提供了一个新的纹理环境GL_COMBINE允许我们显示地控制每一个纹理单元的纹理片段是如何组合的。使用纹理组合器模式代码如下:

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

纹理组合器是通过glTexEnv函数来控制的。然后我们需要设置使用哪个纹理组合器的函数。glTexEnv函数的第二个参数是组合器函数的选择器,可以是GL_COMBINE_RGB或GL_COMBINE_ALPHA。第三个参数是你想使用的纹理环境函数。这个参数值如下表:

常量 函数
GL_REPLACE Arg0
GL_MODULATE Arg0 * Arg1
GL_ADD Arg0 + Arg1
GL_ADD_SIGNED Arg0 + Arg1 – 0.5
GL_INTERPOLATE (Arg0 * Arg2) + (Arg1 * (1-Arg2))
GL_SUBTRACT Arg0 - Arg1
GL_DDT3_RGB/GL_DDT3_RGBA 4*((Arg0r-0.5)*(Arg1r-0.5)+(Arg0g-0.5)*(Arg1g-0.5)+(Arg0b-0.5)*(Arg1b-0.5))

例如你给RGB值选择GL_REPLACE组合器,你的函数调用如下:

glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);

上表中的Arg0——Arg2通过更多的glTexEnv调用来设置。GL_SOURCEx_RGB和GL_SOURCEx_ALPHA值用于指定RGB和alpha组合器函数的参数,x可以是0,1或者2,这些来源的值如下表:

常量 描述
GL_TEXTURE 来源于当前绑定的纹理单元
GL_TEXTUREx 来源于绑定的纹理x
GL_CONSTANT 颜色值或aplha值来源于通过GL_TEXTURE_ENV_COLOR参数设置的值
GL_PRIMARY_COLOR 来源于原几何图形片段
GL_PREVIOUS 来源于前一个纹理单元的纹理环境的结果

例:你设置Arg0为纹理单元0

glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);

我们还可以对给定的源值施加额外的控制。设置这些操作数,我们可以使用常量GL_OPERANDx_RGB或者GL_OPERANDx_ALPHA,x可以是0,1或者2,可以给这些操作数赋予的值如下表:

常量 描述
GL_SRC_COLOR 源颜色值,不能用于GL_OPERANDx_ALPHA
GL_ONE_MINUS_SRC_COLOR 源颜色值的反码(1-value)不能用于GL_OPERANDx_ALPHA
GL_SRC_ALPHA 源alpha值
GL_ONE_MINUS_SRC_ALPHA 源alpha值的反码(1-value)

例如你已经在两个纹理单元中加载了两个纹理,你在应用纹理时,想要把两个纹理的颜色值相乘,你可以如下设置:

//设置纹理环境为纹理组合

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

//告诉OpenGL,对RGB值使用组合函数GL_MODULATE

glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);

//设置arg0为第0个纹理单元

glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);

glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

//设置arg1为纹理单元1

glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE1);

glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR):

我们还可以为纹理组合器,指定一个RGB或alpha的缩放因子。默认情况下是:

glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 1.0f);

glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1.0f);

点精灵

点精灵的特性是OpenGl1.5开始引入的。使用点精灵,我们可以通过绘制一个3D的点来把纹理图像映射的屏幕上。例如原本是一个矩形的几何图元(由四个顶点组成),然后再把2D的纹理映射到这个矩形上才能得到的效果,现在使用点精灵则只需要绘制一个3D的点就可以了。这减少了处理器需要处理的顶点,也减少了顶点传输的带宽,提高了性能。

例如,大量的微粒在屏幕上移动形成魔幻般的视觉效果,可以通过点精灵来实现(常见的屏幕保护程序)。

image

在点精灵之前,要实现这种效果,我们需要在屏幕上绘制大量的纹理多边形,并需要通过对多边形进行旋转,以确保它面对这照相机。而点精灵纹理允许我们绘制一个3D的顶点,来渲染一个完美对齐的纹理2D多边形。

使用点精灵

点精灵非常容易使用,开启点精灵GL_POINT_SPRITE,设置纹理环境的目标位GL_POINT_SPRITE 的GL_COORD_REPLACE参数为真,发送3D点。

glBindTexture(GL_TEXTURE_2D, objectID);

glEnable(GL_POINT_SPRITE);

glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);

glBegin(GL_POINTS);

glEnd();

把之前第六章的smooth样例进行修改,使用点精灵的方式来绘制,代码如下:

#include "gltools.h" #include <math.h>
#include "math3d.h" //屏幕的宽,高 #define SCREEN_X 800 #define SCREEN_Y 600 //大中小星星的数量 #define LARGE_NUM 20 #define MEDIUM_NUM 30 #define SMALL_NUM 40 //星星的坐标 M3DVector2f smallStars[SMALL_NUM];
M3DVector2f mediumStars[MEDIUM_NUM];
M3DVector2f largeStars[LARGE_NUM]; #define TEXNUM 2 #define STAR 0 #define MOON 1
GLuint textureObj[TEXNUM]; void ProcessMenu(int value); void ChangeSize(GLsizei w, GLsizei h)
{ if (h == 0)
    h = 1;

  glViewport(0, 0, w, h);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity(); //设置为2D的正投影,使得坐标从屏幕的左下角开始 gluOrtho2D(0.0, SCREEN_X, 0.0, SCREEN_Y);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glutPostRedisplay();
} void SetupRC()
{
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //随机获取星星的位置  for (int i = 0; i < SMALL_NUM; ++i)
  {
    smallStars[i][0] = (GLfloat)(rand() % SCREEN_X);
    smallStars[i][1] = (GLfloat)(rand() % SCREEN_Y);
  } for (int i = 0; i < MEDIUM_NUM; ++i)
  {
    mediumStars[i][0] = (GLfloat)(rand() % SCREEN_X);
    mediumStars[i][1] = (GLfloat)((rand() % SCREEN_Y) + 50);
  } for (int i = 0; i < LARGE_NUM; ++i)
  {
    largeStars[i][0] = (GLfloat)(rand() % SCREEN_X);
    largeStars[i][1] = (GLfloat)(rand() % SCREEN_Y);
  }

  GLint iWidth, iHeight, iComponents;
  GLenum eFormat; //生成纹理对象 glGenTextures(TEXNUM, textureObj); //加载纹理图片 glBindTexture(GL_TEXTURE_2D, textureObj[STAR]);
  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); void *pImage = gltLoadTGA("..\\images\\star.tga", &iWidth, &iHeight, &iComponents, &eFormat); if (pImage)
  {
    glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pImage);
    free(pImage);
    pImage = NULL;
  }
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  glBindTexture(GL_TEXTURE_2D, textureObj[MOON]);

  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
  pImage = gltLoadTGA("..\\images\\moon.tga", &iWidth, &iHeight, &iComponents, &eFormat); if (pImage)
  {
    glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pImage);
    free(pImage);
    pImage = NULL;
  }
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //启用点精灵 glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
  glEnable(GL_POINT_SPRITE);

  ProcessMenu(3);

} void RenderScene()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glColor3f(1.0f, 1.0f, 1.0f); //绑定纹理,使用点精灵 glBindTexture(GL_TEXTURE_2D, textureObj[STAR]);
  glEnable(GL_POINT_SPRITE); 
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND); //画小星星 glPointSize(7.0);
  glBegin(GL_POINTS); for (int i = 0; i < SMALL_NUM; ++i)
    glVertex2fv(smallStars[i]);
  glEnd(); //画中等大小的星星 glPointSize(12.0);
  glBegin(GL_POINTS); for (int i = 0; i < MEDIUM_NUM; ++i)
  {
    glVertex2fv(mediumStars[i]);
  }
  glEnd(); //大星星 glPointSize(20.0);
  glBegin(GL_POINTS); for (int i = 0; i < LARGE_NUM; ++i)
  {
    glVertex2fv(largeStars[i]);
  }
  glEnd();

  glBindTexture(GL_TEXTURE_2D, textureObj[MOON]); //画月亮 glPointSize(120.0f);

  GLfloat x = 650.0f;
  GLfloat y = 400.0f;
  glBegin(GL_POINTS);
    glVertex2f(x, y);
  glEnd(); //星座连线 glDisable(GL_TEXTURE_2D);
  glDisable(GL_POINT_SPRITE);
  glLineWidth(3.0);
  glBegin(GL_LINE_STRIP);
  glVertex2f(0.0f, 50.0f);
  glVertex2f(50.0f, 150.0f);
  glVertex2f(100.0f, 20.0f);
  glVertex2f(300.0f, 300.0f);
  glVertex2f(450.0f, 100.0f);
  glVertex2f(600.0f, 200.0f);
  glVertex2f(800.0f, 30.0f);
  glEnd();
  glutSwapBuffers();
} void ProcessMenu(int value)
{ switch (value)
  { case 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_TEXTURE_2D);
      glDisable(GL_POINT_SPRITE); break;
    } case 2:
    { //关闭混合 glDisable(GL_BLEND);
      glDisable(GL_POINT_SMOOTH);
      glDisable(GL_LINE_SMOOTH);
      glDisable(GL_POLYGON_SMOOTH);
      glDisable(GL_TEXTURE_2D);
      glDisable(GL_POINT_SPRITE); break;
    } case 3: //点精灵  glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
    glDisable(GL_LINE_SMOOTH);
    glDisable(GL_POINT_SMOOTH);
    glDisable(GL_POLYGON_SMOOTH); break; default: break;
  }

  glutPostRedisplay();
} void ShutdownRC()
{
  glDeleteTextures(TEXNUM, textureObj);
} int main(int args, char **argv)
{
  glutInit(&args, argv);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
  glutInitWindowSize(SCREEN_X, SCREEN_Y);
  glutCreateWindow("POINTSPRITES"); //右键菜单 int menuID = glutCreateMenu(ProcessMenu);
  glutAddMenuEntry("antialiasing", 1);
  glutAddMenuEntry("normal", 2);
  glutAddMenuEntry("pointsprites", 3);
  glutAttachMenu(GLUT_RIGHT_BUTTON);

  glutDisplayFunc(RenderScene);
  glutReshapeFunc(ChangeSize);
  SetupRC();
  glutMainLoop(); return 0;
}

效果如下:

image

纹理应用

点精灵遵循所有的2D纹理的规则,包括纹理环境为GL_DECAL,GL_REPLACE,GL_MODULATE等,以及mipmapped和多重纹理。如果把GL_COORD_REPLACE设置为加,

glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_FALSE);

那么顶点指定单个纹理坐标,并应用于整个点。如果为GL_TRUE,则OpenGL根据点的表面对纹理坐标进行插值。当然这些点的大小必须是大于1.0的。

点精灵参数

点精灵的特性可以通过glPointParameter来进行微调,下图展示应用点精灵的两个不同纹理坐标的原点位置。

image

通过设置GL_POINT_SPRITE_COORD_ORIGIN为GL_LOWER_LEFT把纹理坐标的原点设置到左下角。

glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT);

更多的参数设置参考https://www.opengl.org/sdk/docs/man/xhtml/glPointParameter.xml

OpenGL超级宝典 第4版 中文版PDF+英文版+源代码 见 

OpenGL编程指南(原书第7版)中文扫描版PDF 下载

OpenGL 渲染篇

Ubuntu 13.04 安装 OpenGL

OpenGL三维球体数据生成与绘制【附源码】

Ubuntu下OpenGL编程基础解析

如何在Ubuntu使用eclipse for c++配置OpenGL  

更多《OpenGL超级宝典学习笔记》相关知识 见 

相关内容