glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
// 纹理比较函数是小于等于为GL_TRUE
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
// GL_LUMINANCE 按照亮度值存储纹理单元;fragment时候取输入片断亮度值作为输出片断颜色。
glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE );
// GL_COMPARE_R_TO_TEXTURE指示使片断的r坐标和纹理单元值进行比较,小于等于用亮度1,大于用亮度0
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE );
理论上的:
1)OGL深度缓存中的值也是[0,1]的。
2)纹理坐标其实是渲染的物体身上的,纹理中并不需要纹理坐标数据,只有值即可。
计算纹理坐标时候的:
3)计算纹理坐标匹配时候,r,s,r,q都铺满整个屏幕即可,全部相对于观察各平面的距离,不需要按照之前的渲染来缩放纹理坐标,得到相对整个屏幕大小的纹理的坐标系。
4) 纹理坐标的自动计算,应该在视图坐标系后使用纹理矩阵堆栈对其进行平移->旋转->缩放,透视投影的设置顺序,来在OGL代码内部进行右乘运算,才得到正确的纹理坐标值。下面代码看得迷糊且无法运行,根据自己当前水平的分析下面计算应该是有问题的。或者是自己研究得不够透彻?
不能运行,感觉有错误,仅有参考作用:
#include
#include
#include
#include
#pragma comment(lib, "glew32d.lib")
#include "helpers.h"
#ifdef GL_ARB_shadow
#define GL_TEXTURE_COMPARE_MODE GL_TEXTURE_COMPARE_MODE_ARB
#define GL_TEXTURE_COMPARE_FUNC GL_TEXTURE_COMPARE_FUNC_ARB
#define GL_DEPTH_TEXTURE_MODE GL_DEPTH_TEXTURE_MODE_ARB
#define GL_COMPARE_R_TO_TEXTURE GL_COMPARE_R_TO_TEXTURE_ARB
#endif
#define SHADOW_MAP_WIDTH 256
#define SHADOW_MAP_HEIGHT 256
#define PI 3.14159265359
GLdouble fovy = 60.0;
GLdouble nearPlane = 10.0;
GLdouble farPlane = 100.0;
GLfloat angle = 0.0;
GLfloat torusAngle = 0.0;
GLfloat lightPos[] = { 25.0, 25.0, 25.0, 1.0 };
GLfloat lookat[] = { 0.0, 0.0, 0.0 };
GLfloat up[] = { 0.0, 0.0, 1.0 };
GLboolean showShadow = GL_FALSE;
void
init( void )
{
GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, 0,
GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
glLightfv( GL_LIGHT0, GL_POSITION, lightPos );
glLightfv( GL_LIGHT0, GL_SPECULAR, white );
glLightfv( GL_LIGHT0, GL_DIFFUSE, white );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
// 纹理比较函数是小于等于为GL_TRUE
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
// GL_LUMINANCE 按照亮度值存储纹理单元;fragment时候取输入片断亮度值作为输出片断颜色。
glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE );
// 使片断的r坐标和纹理单元值进行比较,小于等于用亮度1,大于用亮度0
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE );
// 纹理坐标生成时线性的
glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
glCullFace( GL_BACK );
glEnable( GL_DEPTH_TEST );
glEnable( GL_LIGHT0 );
glEnable( GL_LIGHTING );
glEnable( GL_TEXTURE_2D );
glEnable( GL_TEXTURE_GEN_S );
glEnable( GL_TEXTURE_GEN_T );
glEnable( GL_TEXTURE_GEN_R );
glEnable( GL_TEXTURE_GEN_Q );
glEnable( GL_COLOR_MATERIAL );
glEnable( GL_CULL_FACE );
}
void
reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( fovy, (GLdouble) width/height, nearPlane, farPlane );
glMatrixMode( GL_MODELVIEW );
}
void
idle( void )
{
angle += PI / 10000;
torusAngle += .1;
glutPostRedisplay();
}
void
keyboard( unsigned char key, int x, int y )
{
switch( key ) {
case 27: /* Escape */
exit( 0 );
break;
case 't': {
static GLboolean textureOn = GL_TRUE;
textureOn = !textureOn;
if ( textureOn )
glEnable( GL_TEXTURE_2D );
else
glDisable( GL_TEXTURE_2D );
}
break;
case 'm': {
static GLboolean compareMode = GL_TRUE;
compareMode = !compareMode;
printf( "Compare mode %s\n", compareMode ? "On" : "Off" );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
compareMode ? GL_COMPARE_R_TO_TEXTURE : GL_NONE );
}
break;
case 'f': {
static GLboolean funcMode = GL_TRUE;
funcMode = !funcMode;
printf( "Operator %s\n", funcMode ? "GL_LEQUAL" : "GL_GEQUAL" );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC,
funcMode ? GL_LEQUAL : GL_GEQUAL );
}
break;
case 's':
showShadow = !showShadow;
break;
case 'p': {
static GLboolean animate = GL_TRUE;
animate = !animate;
glutIdleFunc( animate ? idle : NULL );
}
break;
}
glutPostRedisplay();
}
void
transposeMatrix( GLfloat m[16] )
{
GLfloat tmp;
#define Swap( a, b ) tmp = a; a = b; b = tmp
Swap( m[1], m[4] );
Swap( m[2], m[8] );
Swap( m[3], m[12] );
Swap( m[6], m[9] );
Swap( m[7], m[13] );
Swap( m[11], m[14] );
#undef Swap
}
void
drawObjects( GLboolean shadowRender )
{
GLboolean textureOn = glIsEnabled( GL_TEXTURE_2D );
if ( shadowRender )
glDisable( GL_TEXTURE_2D );
if ( !shadowRender ) {
glNormal3f( 0, 0, 1 );
glColor3f( 1, 1, 1 );
glRectf( -20.0, -20.0, 20.0, 20.0 );
}
glPushMatrix();
glTranslatef( 11, 11, 11 );
glRotatef( 54.73, -5, 5, 0 );
glRotatef( torusAngle, 1, 0, 0 );
glColor3f( 1, 0, 0 );
glutSolidTorus( 1, 4, 8, 36 );
glPopMatrix();
glPushMatrix();
glTranslatef( 2, 2, 2 );
glColor3f( 0, 0, 1 );
glutSolidCube( 4 );
glPopMatrix();
glPushMatrix();
glTranslatef( lightPos[0], lightPos[1], lightPos[2] );
glColor3f( 1, 1, 1 );
glutWireSphere( 0.5, 6, 6 );
glPopMatrix();
if ( shadowRender && textureOn )
glEnable( GL_TEXTURE_2D );
}
void
generateShadowMap( void )
{
GLint viewport[4];
GLfloat lightPos[4];
glGetLightfv( GL_LIGHT0, GL_POSITION, lightPos );
glGetIntegerv( GL_VIEWPORT, viewport );
// 调整视口大小,使得和设置生成的纹理大小匹配;也是第二次渲染时候受到影响的片段着色区域
glViewport( 0, 0, SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
// 透视投影设置,fovy是80相比标准的90偏小了,也就是将80区域的大小映射到整个屏幕其实是将物体放大了。
// aspect=1,屏幕的宽高比等于1,符合设置的深度纹理大小。zear距离是10,zfar距离是1000.
// 这样会求得一个透视投影矩阵,会将物体坐标缩放到[-1,1]区域包括OGL的z坐标。
// 但是:glDepthRange(near,far),near默认是0,far默认是1,将其变换到[0,1]放入深度缓存中。
gluPerspective( 80.0, 1.0, 10.0, 1000.0 );
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();
// 模型视图矩阵,设置观察点在灯光处,朝向世界坐标原点,up指向屏幕外
gluLookAt( lightPos[0], lightPos[1], lightPos[2],
lookat[0], lookat[1], lookat[2],
up[0], up[1], up[2] );
// 世界坐标中绘制物体
drawObjects( GL_TRUE );
//恢复模型视图矩阵
glPopMatrix();
// 恢复投影矩阵
glMatrixMode( GL_PROJECTION );
glPopMatrix();
// 切换到模型视图坐标系
glMatrixMode( GL_MODELVIEW );
// 将深度纹理拷贝到了当前纹理内存活动的纹理对象中
glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0,
SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, 0 );
// 恢复视口大小
glViewport( viewport[0], viewport[1], viewport[2], viewport[3] );
// 将得到的纹理图像绘制出来,只是观察使用的,绘制歧视位置从中下角开始,因为屏幕大小是512x512
if ( showShadow ) {
GLfloat depthImage[SHADOW_MAP_WIDTH][SHADOW_MAP_HEIGHT];
glReadPixels( 0, 0, SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT,
GL_DEPTH_COMPONENT, GL_FLOAT, depthImage );
glWindowPos2f( viewport[2]/2, 0 );
glDrawPixels( SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, GL_LUMINANCE,
GL_FLOAT, depthImage );
glutSwapBuffers();
}
}
void
generateTextureMatrix( void )
{
GLfloat tmpMatrix[16];
/*
* Set up projective texture matrix. We use the GL_MODELVIEW matrix
* stack and OpenGL matrix commands to make the matrix.
*/
// 自动生成物体的纹理坐标,
// 且将物体在视图坐标系下的z在[-1,1]转换到深度纹理单元值[0,1]之间,用于比较。
glPushMatrix();
glLoadIdentity();
// 连续变换从下面到上,且先平移。且设置纹理矩阵变换,来执行这样的平移旋转缩放+透视投影变换?
// 模型坐标系下,x,y平移0.5;将所有物体都向右向上偏移0.5。
glTranslatef( 0.5, 0.5, 0.0 );// 这里个人觉得是: glTranslatef( 1, 1, 1);
// 模型坐标系下,将所有物体x,y都缩小为原来的1/2; z不变。
glScalef( 0.5, 0.5, 1.0 ); // 这里个人觉得是: glScalef( 0.5, 0.5, 0.5);除非还有什么问题?
gluPerspective( 60.0, 1.0, 1.0, 1000.0 );
// 还是从光源位置观察所有物体
gluLookAt( lightPos[0], lightPos[1], lightPos[2],
lookat[0], lookat[1], lookat[2],
up[0], up[1], up[2] ); // 这里只是简单的坐标系转换
glGetFloatv( GL_MODELVIEW_MATRIX, tmpMatrix );
glPopMatrix();
// 转置模型视图矩阵,也就是矩阵的逆,用于变换到视口坐标系用
transposeMatrix( tmpMatrix );
// 生成的纹理坐标是物体该维度的顶点到该维度的平面的距离 = p1x0 + p2y0 + p3z0 + p4w0
// p向量是平面系数; x0, y0, z0, w0是物体的顶点坐标;这里的tmpMatrix刚好是视图坐标系的基向量。
// 自动计算物体的深度坐标系s,t,r,q;s' = s / q; t'= t / q, r = r'/q;
// s,t是光栅化的位置,在视图坐标系中的距离,还需要进一步乘以纹理矩阵堆栈计算的就是透视和缩放。
glTexGenfv( GL_S, GL_OBJECT_PLANE, &tmpMatrix[0] );
glTexGenfv( GL_T, GL_OBJECT_PLANE, &tmpMatrix[4] );
// r刚好是物体到观察点的距离,在视图坐标系中的值。
glTexGenfv( GL_R, GL_OBJECT_PLANE, &tmpMatrix[8] );
glTexGenfv( GL_Q, GL_OBJECT_PLANE, &tmpMatrix[12] );
}
void
display( void )
{
GLfloat radius = 30;
// 第一次绘制生成了深度纹理和纹理坐标
generateShadowMap();
generateTextureMatrix();
if ( showShadow )
return;
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// 第二次绘制物体,使用深度纹理和深度纹理设置的纹理采样函数
glPushMatrix();
// 从各个位置观察物体
gluLookAt( radius*cos(angle), radius*sin(angle), 30,
lookat[0], lookat[1], lookat[2],
up[0], up[1], up[2] );
drawObjects( GL_FALSE );
glPopMatrix();
glutSwapBuffers();
}
int
main( int argc, char** argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 512, 512 );
glutInitWindowPosition( 100, 100 );
glutCreateWindow( argv[0] );
init();
glutDisplayFunc( display );
glutReshapeFunc( reshape );
glutKeyboardFunc( keyboard );
glutIdleFunc( idle );
glutMainLoop();
return 0;
}
上面描述的是用深度纹理来绘制阴影,也就是得到阴影的纹理贴图。
但是阴影的顶点信息,需要考虑光源,投射平面;光源类型是平行光则是正交投影,光源是点光源或聚光灯则是透视投影。
顶点信息,通过对需要绘制阴影的物体,进行阴影矩阵变换,来绘制物体的阴影顶点信息。
在DX中可以用:D3DMatrixShadow来获取,需要传入光源类型方向,投影到的平面定义,传出阴影矩阵。在OGL中应该也有相应的矩阵。
绘制了阴影的顶点信息时,可以传入阴影纹理贴图或传入颜色就会得到想要的阴影;更加简单的阴影就是圆底贴图,一般性能承受不起时候采用。
静态的复杂背景的绘制,从已有图像绘制比将复杂背景物体重新绘制要高效。从后台缓存中拷贝得到背景色的贴图和保存背景色的深度缓存。绘制方法是,先绘制前景色,开启深度测试和写入深度缓存,不写入模板缓存,这个时候深度缓存是前景色。开启深度测试和写入深度缓存,同时开启模板测试和写入模板缓存,禁止颜色缓存写入,用glDrawPixels将背景的深度值写入深度缓存,这个时候写入了模板缓存得到模板缓存值。最后绘制背景图像,根据模板缓存写入,得到结果。
动态的图像,需要glPixelTransfer来实现,但是动态背景还是不用深度缓存绘制为好, 一般绘制动的物体,背景用地图来实现。