OpenGL超级宝典笔记——深度纹理和阴影 【转】

目录[-]

之前我们介绍过简单的把物体压平到投影平面来制造阴影。但这种阴影方式有其局限性(如投影平面须是平面)。在OpenGL1.4引入了一种新的方法阴影贴图来产生阴影。

阴影贴图背后的原理是简单的。我们先把光源的位置当作照相机的位置,我们从这个位置观察物体,我们就知道哪些物体的表面是被照射到(被光源看到) 的,哪些是没有被照射到(被遮挡住)的(在某个方向上离光源最近的表面是被照射的,后面的表面则没有被照射到)。我们开启深度测试,这样我们就可以得到一 个有用的深度缓冲区数据(每一个像素在深度缓冲区中的结果),然后我们从深度缓冲区中读取数据作为一个阴影纹理,投影回场景中,然后我们在使用照相机的视 角,来渲染物体。

光源视角

首先我们把视角移到光源的位置。我们可以通过glu库的辅助函数:

gluLookAt(lightPos[0], lightPos[1], lightPos[2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

把光源的位置设置为观察的位置。

为了以最佳的方式利用空间来产生阴影贴图。从光源的角度看过去的透视可视区域要适应窗口的比例,且透视的最近平面位置是里光源最近的物体的平面,最 远的平面位置是离光源最远的物体的平面。这样我们就可以充分的利用场景的信息来填充深度缓冲区,来制造阴影贴图。我们估计恰好包好整个场景的视野。

//场景的半径大小
GLfloat sceneBoundingRadius = 95.0f;

//光的距离
lightToSceneDistance = sqrt(lightPos[0] * lightPos[0] +
												lightPos[1] * lightPos[1] +
												lightPos[2] * lightPos[2]);

//近裁剪平面
nearPlane = lightToSceneDistance - sceneBoundingRadius;
//让场景充满整个深度纹理
fieldOfView = (GLfloat)m3dRadToDeg(2.0f * atan(sceneBoundingRadius/lightToSceneDistance));

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fieldOfView, 1.0f, nearPlane, nearPlane + (2.0f * sceneBoundingRadius));

在上面的代码中,场景的中心位于原点,场景中所有的物体,在以原点为中心,半径为sceneBoundingRadius的圆中。这是我们对场景的粗略估计。大致如下图:

因为我们只需要得到像素经过深度测试后,深度缓冲区的结果。所以我们可以去掉一切不必要的的细节,不往颜色缓冲区中写数据因为不需要显示。

glShadeModel(GL_FLAT);
glDisable(GL_LIGHTING);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_NORMALIZE);
glColorMask(0,0,0,0);
...

如果我们可以看到深度缓冲区,深度缓冲区的灰度图大概是这样子的。

新型的纹理

我们需要拷贝深度的数据到纹理中作为阴影贴图。在OpenGL1.4之后,glCopyTexImage2D允许我们从深度缓冲区中拷贝数据。纹理 数据多了一种深度纹理的类型,其内部格式包括 GL_DEPTH_COMPONENT16,GL_DEPTH_COMPONENT24,GL_DEPTH_COMPONENT32,数字代表每个纹理单 元包含的位数。一般情况下,我们希望其内部格式与深度缓冲区的精度相匹配。OpenGL允许你指定通用的GL_DEPTH_COMPONENT格式来匹配 你的深度缓冲区。在以光源的视角绘制后,我们把深度缓冲区的数据拷贝出来作为深度纹理:

glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, shadowWidth, shadowHeight, 0);

只有在物体移动或者光源移动时,才需要重新产生深度纹理。如果仅仅是照相机移动,我们并不需要重新产生深度纹理,因为以光源的角度来看,深度纹理没有变化。当窗口的大小改变时,我们也需要产生一个更大的深度纹理。

深度纹理的大小

在OpenGL2.0之前,在不支持非二次幂的纹理(GL_ARB_texture_non_power_of_two)的扩展的情况下,我们需要调整深度纹理的大小,使其恰好为二次幂。例如在1024x768的分辨率下,最大的二次幂纹理大小是1024x512.

void ChangeSize(int w, int h)
{
	windowWidth = shadowWidth = w;
	windowHeight = shadowHeight  = h;
	//不支持非二次幂纹理大小
	if(!nptTextureAvailable)
	{
		int i = 0;
		int j = 0;
		//获得二次幂的宽度
		while((1 << i) <= shadowWidth )
			i++;

		shadowWidth = (1 << (i-1));
		//二次幂的高度
		while((1 << j) <= shadowHeight )
			j++;

		shadowHeight = (1 << (j-1));
	}

}

首先绘制阴影

如果阴影被定义为完全没有光照的,那么我们不需要绘制它。例如只有单一的聚光灯作为光源,那让阴影是全黑色的就足以满足我们的要求了。如果我们不希 望阴影是全黑的,而且需要阴影区域中的一些细节,那么我们需要在场景中模拟一些环境光。同时,我们还添加一些散射光,帮助传递形状的信息。

GLfloat lowAmbient[4] = {0.1f, 0.1f, 0.1f, 1.0f};
GLfloat lowDiffuse[4] = {0.35f, 0.35f, 0.35f, 1.0f};

glLightfv(GL_LIGHT0, GL_AMBIENT, lowAmbient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lowDiffuse);

//在场景中绘制物体
DrawModels()

PS:此时我们并不需要交换缓冲区(swapbuffers).

如果显示出来是这样子的。

有些OpenGL实现支持一种GL_ARB_shadow_ambient扩展,它可以使我们不必进行第一遍的阴影绘图。

然后是光照

目前我们有了一个很昏暗的场景,要制造阴影,我需要一个明亮的光照区域,来与阴影区形成对比。如何决定这个接受更强光照的区域是阴影贴图的关键。在这个明亮的区域,我们用两倍于阴影的光照强度进行绘制。

GLfloat ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f};
GLfloat diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f};
...
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
 
这样得到的阴影不全是黑色的。

如果去掉前面的绘制阴影的结果是:

投影阴影贴图

我们的目的是需要把阴影贴图投影到场景中(从照相机的位置看)。投影这些代表着光源到被光照射到的第一个物体的距离的深度值。把纹理坐标重定向到正确的坐标空间需要一些数学知识。之前我们解释了把顶点从物体空间变换到视觉空间,再变换到裁剪空间,然后变换到规格化的设备坐标,最后变换到窗口空间的过程。在这里有两组不同的变换矩阵,一组用于变换到照相机的视觉空间,一组用于变换到光源的视觉空间。通过这两组矩阵变换得到两个从不同角度观察的场景。

上面的箭头表示了我们需要应用到视觉线性纹理坐标的变换过程。纹理的投影通常是从视觉线性坐标的产生开始的。这个过程是自动产生纹理坐标的。不同于物体线性纹理坐标的生成,视觉线性坐标的生成并不固定到任何几何图形之上。反之,它好像是一台投影仪把纹理投影到场景中,想象一下你在投影仪前走动的时候,屏幕上会出现不规则的身体形状。

投影纹理映射:

现在我们获得在照相机的视觉空间下顶点对应的纹理坐标。那我们需要进行一些变换来得到顶点的纹理坐标。当前我们在照相机机的视觉空间,首先我们通过视图矩阵的逆变换回到世界坐标系,然后再变换到光源的视觉空间,然后到光源的裁剪空间。这一系列的变换可以通过下面的矩阵相乘得到:

M = Plight * MVlight * MVcamara-1

裁剪空间规格化后的x,y,z的坐标范围在[-1, 1]之间,然而我们的纹理坐标范围为[0,1],所以我们还需要把[-1,1]变换到[0,1]的范围,这个变换很简单,我们只需要把[-1,1]缩放一半(S),然后偏移0.5就可以得到[0,1]了(B)。

M = B * S * Plight * MVlight * MVcamara-1

所以我们可以得到顶点经过变换后的纹理坐标。T1 = M * T;

图解过程如下:

PS: 当前模型视图矩阵的逆矩阵的乘法操作已经包含在了视觉平面方程式中。

即在OpenGL的纹理自动生成模式GL_EYE_LINEAR中,每一个觉平面方程式(eye plane equation)会自动乘以MVcamara-1 

实现上面的步骤一种方式是手动的通过glTranslatef, glScalef, glMultMatrixf 来一步步的实现。另一个方式是在纹理自动生成中,我们可以通过设置一个纹理矩阵来实现上面的变换,把这个纹理矩阵作为视觉线性坐标的视觉平面方程GL_EYE_PLANE即可。

M = B * S * Plight * MVlight 大致代码如下:

M3DMatrix44f tempMatrix;
m3dLoadIdentity44(tempMatrix);
//偏移0.5
m3dTranslateMatrix44(tempMatrix, 0.5f, 0.5f, 0.5f);
//缩放0.5
m3dScaleMatrix44(tempMatrix, 0.5f, 0.5f, 0.5f);
//乘以光源投影矩阵
m3dMatrixMultiply44(textureMatrix, tempMatrix, lightProjection);
//乘以光源视图矩阵
m3dMatrixMultiply44(tempMatrix, textureMatrix, lightModelView);
//矩阵转置,获得平面方程的s,t,r和q行
m3dTransposeMatrix44(textureMatrix, tempMatrix);

应用到视觉平面中:

//因为在当前模型视图矩阵的逆矩阵的乘法操作已经包含在了视觉平面方程式中
//确保在glTexGenfv前已经设置好照相机的模型视图矩阵。
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(cameraPos[0], cameraPos[1], cameraPos[2],
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
...

//为阴影贴图的投影设置视觉平面
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
glTexGenfv(GL_S, GL_EYE_PLANE, &textureMatrix[0]);
glTexGenfv(GL_T, GL_EYE_PLANE, &textureMatrix[4]);
glTexGenfv(GL_R, GL_EYE_PLANE, &textureMatrix[8]);
glTexGenfv(GL_Q, GL_EYE_PLANE, &textureMatrix[12]);
...
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);

阴影比较

现在我们如何知道从照相机视角看到的点是否在阴影中呢。从上面的那些步骤来看,我们已知顶点的深度纹理坐标,那么这个深度纹理坐标对应的在深度纹理的值我们可以知道即texture[s/q, t/q],这个深度纹理记录了在光的角度看过去离光源最近的点的深度值,我们是设置的深度比较函数是glDepthFunc(GL_LEQUAL);。,同时我们知道(r/q)是顶点在真实光源中深度值,已经通过缩放和偏移变换到了[0,1]的范围。然后我们比较texture[s/q, t/q]和(r/q)如果texture[s/q, t/q] < r/q那么就表示这个点在阴影中。如下图:

深度纹理只包含了一个值代表深度。但在纹理环境的纹理查询中,我们需要返回四个成分的值(RGBA)。OpenGL提供了几种方式把这单个深度值扩展到其他的通道中,其中包含GL_ALPHA(0,0,0,D),GL_LUMINANCE(D,D,D,1)和GL_INTENSITY(D,D,D,D)。在这里我们把深度值扩展到所有的深度通道。

glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INSTENSITY);

在OpenGL中开启阴影比较,来产生阴影效果。我们把深度值与纹理坐标的R成分进行比较。

//设置阴影比较

glEnable(GL_TEXTURE_2D);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);

效果:

书中部分的代码示例:

// Called to regenerate the shadow map
void RegenerateShadowMap(void)
{
  GLfloat lightToSceneDistance, nearPlane, fieldOfView;
  GLfloat lightModelview[16], lightProjection[16];
  GLfloat sceneBoundingRadius = 95.0f; // based on objects in scene

  // Save the depth precision for where it‘s useful
  lightToSceneDistance = sqrt(lightPos[0] * lightPos[0] + 
    lightPos[1] * lightPos[1] + 
    lightPos[2] * lightPos[2]);
  nearPlane = lightToSceneDistance - sceneBoundingRadius;
  // Keep the scene filling the depth texture
  fieldOfView = (GLfloat)m3dRadToDeg(2.0f * atan(sceneBoundingRadius / lightToSceneDistance));

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(fieldOfView, 1.0f, nearPlane, nearPlane + (2.0f * sceneBoundingRadius));
  glGetFloatv(GL_PROJECTION_MATRIX, lightProjection);
  // Switch to light‘s point of view
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(lightPos[0], lightPos[1], lightPos[2], 
    0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
  glGetFloatv(GL_MODELVIEW_MATRIX, lightModelview);
  glViewport(0, 0, shadowWidth, shadowHeight);

  // Clear the depth buffer only
  glClear(GL_DEPTH_BUFFER_BIT);

  // All we care about here is resulting depth values
  glShadeModel(GL_FLAT);
  glDisable(GL_LIGHTING);
  glDisable(GL_COLOR_MATERIAL);
  glDisable(GL_NORMALIZE);
  glColorMask(0, 0, 0, 0);

  // Overcome imprecision
  glEnable(GL_POLYGON_OFFSET_FILL);

  // Draw objects in the scene except base plane
  // which never shadows anything
  DrawModels(GL_FALSE);

  // Copy depth values into depth texture
  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 
    0, 0, shadowWidth, shadowHeight, 0);

  // Restore normal drawing state
  glShadeModel(GL_SMOOTH);
  glEnable(GL_LIGHTING);
  glEnable(GL_COLOR_MATERIAL);
  glEnable(GL_NORMALIZE);
  glColorMask(1, 1, 1, 1);
  glDisable(GL_POLYGON_OFFSET_FILL);

  // Set up texture matrix for shadow map projection,
  // which will be rolled into the eye linear
  // texture coordinate generation plane equations
  M3DMatrix44f tempMatrix;
  m3dLoadIdentity44(tempMatrix);
  m3dTranslateMatrix44(tempMatrix, 0.5f, 0.5f, 0.5f);
  m3dScaleMatrix44(tempMatrix, 0.5f, 0.5f, 0.5f);
  m3dMatrixMultiply44(textureMatrix, tempMatrix, lightProjection);
  m3dMatrixMultiply44(tempMatrix, textureMatrix, lightModelview);
  // transpose to get the s, t, r, and q rows for plane equations
  m3dTransposeMatrix44(textureMatrix, tempMatrix);
}

// Called to draw scene
void RenderScene(void)
{
  // Track camera angle
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if (windowWidth > windowHeight)
  {
    GLdouble ar = (GLdouble)windowWidth / (GLdouble)windowHeight;
    glFrustum(-ar * cameraZoom, ar * cameraZoom, -cameraZoom, cameraZoom, 1.0, 1000.0);
  }
  else
  {
    GLdouble ar = (GLdouble)windowHeight / (GLdouble)windowWidth;
    glFrustum(-cameraZoom, cameraZoom, -ar * cameraZoom, ar * cameraZoom, 1.0, 1000.0);
  }

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(cameraPos[0], cameraPos[1], cameraPos[2], 
    0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

  glViewport(0, 0, windowWidth, windowHeight);

  // Track light position
  glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

  // Clear the window with current clearing color
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  if (showShadowMap)
  {
    // Display shadow map for educational purposes
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadIdentity();
    glEnable(GL_TEXTURE_2D);
    glDisable(GL_LIGHTING);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
    // Show the shadowMap at its actual size relative to window
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(-1.0f, -1.0f);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(((GLfloat)shadowWidth/(GLfloat)windowWidth)*2.0f-1.0f, 
      -1.0f);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(((GLfloat)shadowWidth/(GLfloat)windowWidth)*2.0f-1.0f, 
      ((GLfloat)shadowHeight/(GLfloat)windowHeight)*2.0f-1.0f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(-1.0f, 
      ((GLfloat)shadowHeight/(GLfloat)windowHeight)*2.0f-1.0f);
    glEnd();
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_LIGHTING);
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    gluPerspective(45.0f, 1.0f, 1.0f, 1000.0f);
    glMatrixMode(GL_MODELVIEW);
  }
  else if (noShadows)
  {
    // Set up some simple lighting
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);

    // Draw objects in the scene including base plane
    DrawModels(GL_TRUE);
  }
  else
  {
    if (!ambientShadowAvailable)
    {
      GLfloat lowAmbient[4] = {0.1f, 0.1f, 0.1f, 1.0f};
      GLfloat lowDiffuse[4] = {0.35f, 0.35f, 0.35f, 1.0f};

      // Because there is no support for an "ambient"
      // shadow compare fail value, we‘ll have to
      // draw an ambient pass first...
      glLightfv(GL_LIGHT0, GL_AMBIENT, lowAmbient);
      glLightfv(GL_LIGHT0, GL_DIFFUSE, lowDiffuse);

      // Draw objects in the scene, including base plane
      DrawModels(GL_TRUE);

      // Enable alpha test so that shadowed fragments are discarded
      glAlphaFunc(GL_GREATER, 0.9f);
      glEnable(GL_ALPHA_TEST);
    }

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);

    // Set up shadow comparison
    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, 
      GL_COMPARE_R_TO_TEXTURE);

    // Set up the eye plane for projecting the shadow map on the scene
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    glEnable(GL_TEXTURE_GEN_R);
    glEnable(GL_TEXTURE_GEN_Q);
    glTexGenfv(GL_S, GL_EYE_PLANE, &textureMatrix[0]);
    glTexGenfv(GL_T, GL_EYE_PLANE, &textureMatrix[4]);
    glTexGenfv(GL_R, GL_EYE_PLANE, &textureMatrix[8]);
    glTexGenfv(GL_Q, GL_EYE_PLANE, &textureMatrix[12]);

    // Draw objects in the scene, including base plane
    DrawModels(GL_TRUE);

    glDisable(GL_ALPHA_TEST);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_GEN_R);
    glDisable(GL_TEXTURE_GEN_Q);
  }

  if (glGetError() != GL_NO_ERROR)
    fprintf(stderr, "GL Error!\n");

  // Flush drawing commands
  glutSwapBuffers();
}

完整代码地址https://github.com/sweetdark/openglex/tree/master/shadowmap

表述能力有限。如果错误,请指正不胜感激。详细的请参考下面的链接。

投影映射纹理GL_EYE_LINEAR的参考:

http://blog.csdn.net/liu_lin_xm/article/details/4850526

http://blog.csdn.net/xukunn1226/article/details/775644

英文http://www.nvidia.com/object/Projective_Texture_Mapping.html

阴影贴图的参考:

http://www.eng.utah.edu/~cs5610/lectures/ShadowMapping%20OpenGL%202009.pdf

ftp://download.nvidia.com/developer/presentations/2004/GPU_Jackpot/Shadow_Mapping.pdf

时间: 2024-10-05 04:40:35

OpenGL超级宝典笔记——深度纹理和阴影 【转】的相关文章

【转载】OpenGL超级宝典笔记——GLSL语言基础

变量 GLSL的变量命名方式与C语言类似.变量的名称可以使用字母,数字以及下划线,但变量名不能以数字开头,还有变量名不能以gl_作为前缀,这个是GLSL保留的前缀,用于GLSL的内部变量.当然还有一些GLSL保留的名称是不能够作为变量的名称的. 基本类型 除了布尔型,整型,浮点型基本类型外,GLSL还引入了一些在着色器中经常用到的类型作为基本类型.这些基本类型都可以作为结构体内部的类型.如下表: 类型 描述 void 跟C语言的void类似,表示空类型.作为函数的返回类型,表示这个函数不返回值.

OpenGL超级宝典笔记三 - 基础纹理1

纹理对象的使用:纹理图像数据的类型,图像数据的导入导出方式,纹理的使用过程:图像数据的加载,纹理参数的设置,纹理的绑定. 一.图像数据的分类:位图图像和像素图像 二.像素图像数据的包装: 在OpenGL中:使用4字节的行对齐方式,每一行补全到4的倍数,使用glPixelStorei(pname,param)可以改变或者恢复像素的存储方式 介绍常用的两组:内存<->缓冲区的压缩和解包 glPixcelStorei(GL_UNPACK_ALIGNMENT) ----图像数据从内存到缓冲区的解包设置

OpenGL超级宝典笔记四 - 基础纹理2

一.使用MipMap贴图(主要是应用在纹理缩小过滤中) 优点: 防止当纹理缩小太大的时候,出现闪烁的问题 提高性能,需要缩小的时候就不加载大内存的纹理 缺点: 需要额外的内存作为代价 组成:由一系列缩小的纹理图像组成,子层是父层的1/4 过滤类型:NEAREST快速,LINEAR效果好 GL_NEAREST_MINMAP_NEAREST选择最邻近Mip层并执行邻近过滤 GL_NEAREST_MINMAP_LINEAR选择最Mip层线性插补并执行邻近过滤 GL_LINEAR_MINMAP_NEAR

OpenGL超级宝典笔记----渲染管线

在OpenGL中任何事物都在3D空间中,但是屏幕和窗口是一个2D像素阵列,所以OpenGL的大部分工作都是关于如何把3D坐标转变为适应你屏幕的2D像素.3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线完成的.图像渲染管线可以被划分为两个主要部分:第一个部分把你的3D坐标转换为2D坐标,第二部分是把2D坐标转变为实际的有颜色的像素. 渲染管线接收一组3D坐标,然后把它们转变为你屏幕上的有色2D像素.渲染管线可以被划分为几个阶段,每个阶段需要把前一阶段的输出作为输入.所有这些阶段都是高度

OpenGL超级宝典笔记——遮挡查询 [转]

目录[-] 遮挡查询之前 包围体 遮挡查询 在一个场景中,如果有有些物体被其他物体遮住了不可见.那么我们就不需要绘制它.在复杂的场景中,这可以减少大量的顶点和像素的处理,大幅度的提高帧率.遮挡查询就是允许我们判断一组图形在进行了深度测试之后是否可见. 遮挡查询之前 为了显示遮挡查询对性能的提升,我们需要一个对照组(不使用遮挡查询来渲染场景). 首先我们先绘制“主遮挡物”.这个主遮挡物不需要太多的细节,一般是墙,天花板,地板之类的物体.在下面的例子中我们,使用6面墙来组成这个主遮挡物. void 

OpenGL超级宝典笔记一 - 基础渲染

1.渲染中的双缓冲: 使用但缓冲的时候,在渲染每一帧的绘图时,会对画板进行擦除然后在慢慢填充绘制,如果绘制时间过长的时候,就会出现闪烁的现象.为解决这个问题,所以引入双缓冲 双缓冲相当于,在显示的画板中重新创建另外一个画板,绘制的过程在另外的画板进行,绘制完成之后,将显示的画板的内容直接替换成另外一个画板的内容.这样呈现的过程中就不会出现闪烁的问题,即使是绘制的过程比较慢,最多就是出现顿的现象 在例子中渲染的RenderScene()函数中,每次渲染结束之后都会调用glutSwapBuffers

OpenGL超级宝典笔记——贝塞尔曲线和曲面(转)

http://my.oschina.net/sweetdark/blog/183721 参数方程表现形式 在中学的时候,我们都学习过直线的参数方程:y = kx + b;其中k表示斜率,b表示截距(即与y轴的交点坐标).类似地,我们也可以用一个参数方程来表示一条曲线.1962年,法国工程师贝塞尔发明了贝塞尔曲线方程.关于贝塞尔曲线的详细介绍可以参考(维基贝塞尔).这里只介绍OpenGL实现贝塞尔的函数. OpenGl定义一条曲线时,也把它定义为一个曲线方程.我们把这条曲线的参数成为u,它的值域就

OpenGL超级宝典笔记——画三角形(转)

http://my.oschina.net/sweetdark/blog/161002 学习了画线的知识,我们可以使用GL_LINE_LOOP来画闭合的多边形.但是使用这种方式画出来的只有线框,多边形没有填充颜色.OpenGL支持绘制实心的多边形,并使用当前的颜色进行填充. 三角形 简单的三角形,需要指定三个顶点. 1: glBegin(GL_TRIANGLES); 2: glVertex2f(0.0f, 0.0f); // V0 3: glVertex2f(25.0f, 25.0f); //

OpenGL超级宝典笔记二 - 基础变换

1.向量: 点乘:float m3dDotProduce3(u,v):返回两个单位向量的余弦值 叉乘:float m3dCrossProduct3(result,u,v):返回垂直于两个向量定义的平面的向量 2.矩阵: OpenGL使用的是列优先排序的矩阵 单位矩阵(对角线为1,其他为0):任何向量乘以一个单位矩阵都不会发生任何改变 3.变换:最终获得的变换矩阵会应用到每个顶点 视图矩阵x模型矩阵x投影矩阵->投影摄像机的位置变换,物体对象的位置变换,投影裁剪变换 若顶点向量为Vert,则变换公