用glTranslate,glRotate来模拟gluLookAt,即gluLookAt的模视矩阵能由glTranslate,glRotate实现:
float t= pow( 3, 0.5);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
glLoadIdentity(); //重置当前的模型观察矩阵
gluLookAt( t* 3, 2.0f, 3.0f, 0.0f, 8.0f, 0.0f, 0.0f, 1.0f, 0.0f);
double adModelMatrix[ 10][ 16];
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix[ 0]);//模視矩陣
glLoadIdentity(); //重置当前的模型观察矩阵
glRotatef(-45,1.0f,0.0f,0.0f);//绕y轴旋转三角形
glRotatef(-60,0.0f,1.0f,0.0f);//绕y轴旋转三角形
glTranslatef(-t* 3,-2.0,-3.0); //左移1.5单位,并移入屏幕6.0
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix[ 1]);//模視矩陣
///////////////////////////////////////////////////////////////////////////////
////RRT(任意角度,R表示旋转glRotate,T表示平移glTranslate)
Vec3 ptEye, ptRef, ptDir;
//视点和参考点任意(取随机数)
ptEye.x= ( rand()% 1000+ 1)/ 10;
ptEye.y= ( rand()% 1000+ 1)/ 10;
ptEye.z= ( rand()% 1000+ 1)/ 10;
ptRef.x= ( rand()% 1000+ 1)/ 10;
ptRef.y= ( rand()% 1000+ 1)/ 10;
ptRef.z= ( rand()% 1000+ 1)/ 10;
ptDir.x= ptRef.x- ptEye.x;
ptDir.y= ptRef.y- ptEye.y;
ptDir.z= ptRef.z- ptEye.z;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
glLoadIdentity(); //重置当前的模型观察矩阵
gluLookAt( ptEye.x, ptEye.y, ptEye.z, ptRef.x, ptRef.y, ptRef.z, 0, 1.0f, 0.0f);
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix[ 0]);//获取模視矩陣
glLoadIdentity(); //重置当前的模型观察矩阵
constdouble PI= 3.14159265358979;
double a1= 90- acos( ptDir.y/ sqrt(ptDir.z* ptDir.z+ ptDir.x* ptDir.x+ ptDir.y* ptDir.y))
* 180/ PI;//竖直角度
double dx= -ptDir.x, dz= -ptDir.z;
double a2= acos( dz/ sqrt( dz* dz+ dx* dx)) * 180/ PI;//水平角度
if ( dx < 0)
{
a2= -a2;
}
glRotatef(-a1,1.0f,0.0f,0.0f);//绕x轴旋转-a1,如-45度
glRotatef(-a2,0.0f,1.0f,0.0f);//绕y轴旋转-a2,如-60度
glTranslatef(-ptEye.x, -ptEye.y, -ptEye.z);//反向平移
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix[ 1]);//模視矩陣
for (int i= 0; i< 16; ++i)
{
if ( abs(adModelMatrix[ 1][ i] - adModelMatrix[ 0][ i] ) >= 1e-4)
{
float t= 0;
++t;
}
}
//经验证,gluLookAt的模视矩阵确实能由glTranslate,glRotate实现,glRotate旋转时对角度有误差,
//但不是很大,一般可在e-4内.
―――――
获取模视矩阵:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt( -2.0f, 0.0f, 0.0f, -2.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
double adModelMatrix[ 16];
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix);//获取模視矩陣
/*此例中的模视矩阵为:
1 0 0 2
0 1 0 0
0 0 1 0
0 0 0 1
*/
如下图:
可以看出gluLookAt()将视点由(0,0,0)向左移动到(-2,0,0),相当于模视矩阵MV为向右移动2个单位。模视矩阵MV是作用于物体点的,对点p,它在视点坐标系下的新点p’=MV*p,相当于把物体向右移动2个单元,也即视点向左移动2个单元。就是说,对视点(及其局部opengl坐标系)经过先平移T、再旋转R后,得到的综合变换矩阵为A(如A=T*R),则最终设置的视图矩阵实际为setViewMatrix(A~)。
附:
OpenGL中的矩阵:
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]
//////////////////////
完整源代码如下:
#include<windows.h>
#include<cmath>
//my addings
#include<gl/gl.h>
#include<gl/glu.h>
#pragmacomment(lib,"opengl32.lib")
#pragmacomment(lib,"glu32.lib")
HDC g_hDC=NULL; // 窗口着色描述表句柄
HGLRC g_hRC=NULL; // OpenGL渲染描述表句柄
HWND g_hWnd=NULL; // 保存我们的窗口句柄
HINSTANCE g_hInstance;
static TCHAR szAppName[] = TEXT ("HelloWin") ; //程序标题
struct Vec3
{
float x;
float y;
float z;
};
//#include <GL/glaux.h>
//#include <gl/glut.h> //包含OpenGL实用库
//#pragma comment(lib, "glaux.lib")
//my addings
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
ATOM RegistWnd( HINSTANCE hInstance);//注册窗口
BOOL InitWnd( HINSTANCE hInstance,int iCmdShow);//窗口初始化
GLvoid ReSizeGLScene(GLsizei width, GLsizei height); //重置OpenGL窗口大小
int InitGL(GLvoid) ; // 此处开始对OpenGL进行所有设置
int DrawGLScene(GLvoid) ; // 从这里开始进行所有的绘制
GLvoid KillGLWindow(GLvoid) ; // 正常销毁窗口
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine,int iCmdShow)
{
HWND hwnd ;
MSG msg ;
g_hInstance= hInstance;
if (! RegistWnd( g_hInstance))
{
MessageBox ( NULL, TEXT ("窗口注册失败"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
if ( !InitWnd( g_hInstance, iCmdShow))
{
MessageBox ( NULL, TEXT ("窗口初始化失败"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
while ( GetMessage(&msg, NULL,0,0) )//获得消息
{
TranslateMessage(&msg);
DispatchMessage(&msg);
DrawGLScene(); //绘制场景
SwapBuffers( g_hDC); //交换缓存(双缓存)
::glFlush();
}
return (int) msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch ( message)
{
case WM_SIZE: //调整OpenGL窗口大小
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width,HiWord=Height
return 0; //返回
}
case WM_DESTROY: //只有用这个才能真正响应窗口上面的关闭按纽,完全退出,否则该程序仍在运行.
PostQuitMessage(0);
return 0;
case WM_CLOSE:
KillGLWindow();
//DestroyWindow( g_hWnd );
//PostQuitMessage( 0);
break;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
//注册窗口
ATOM RegistWnd( HINSTANCE hInstance)
{
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = CreateSolidBrush( RGB( 204 , 232, 207));//绿色
wndclass.lpszClassName = szAppName ;
wndclass.lpszMenuName = NULL ;
return RegisterClass( &wndclass);
}
//注册窗口
//窗口初始化
BOOL InitWnd( HINSTANCE hInstance,int iCmdShow)
{
g_hWnd = CreateWindow( szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT,// initial x position
CW_USEDEFAULT,// initial y position
CW_USEDEFAULT,// initial x size
CW_USEDEFAULT,// initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
if ( !g_hWnd)
{
KillGLWindow();
return FALSE;
}
int bits= 16;
GLuint PixelFormat; // 保存查找匹配的结果
staticPIXELFORMATDESCRIPTOR pfd= //pfd告诉窗口我们所希望的东东,即窗口使用的像素格式
{
sizeof(PIXELFORMATDESCRIPTOR), //上述格式描述符的大小
1, //版本号
PFD_DRAW_TO_WINDOW | //格式支持窗口
PFD_SUPPORT_OPENGL | //格式必须支持OpenGL
PFD_DOUBLEBUFFER, //必须支持双缓冲
PFD_TYPE_RGBA, //申请RGBA格式
bits, //选定色彩深度
0, 0, 0, 0, 0, 0, //忽略的色彩位
0, //无Alpha缓存
0, //忽略Shift Bit
0, //无累加缓存
0, 0, 0, 0, //忽略聚集位
16, // 16位Z-缓存(深度缓存)
0, //无蒙板缓存
0, //无辅助缓存
PFD_MAIN_PLANE, //主绘图层
0, //不使用重叠层
0, 0, 0 //忽略层遮罩
};
if (!( g_hDC=GetDC(g_hWnd))) //取得设备描述表了么?
{
KillGLWindow(); //重置显示区
//MessageBox(NULL,"不能创建一个窗口设备描述表","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //返回FALSE
}
if (!(PixelFormat=ChoosePixelFormat(g_hDC,&pfd))) // Windows 找到相应的象素格式了吗?
{
KillGLWindow(); //重置显示区
//MessageBox(NULL,"不能创建一种相匹配的像素格式","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //返回FALSE
}
if(!SetPixelFormat(g_hDC,PixelFormat,&pfd)) //能够设置象素格式么?
{
KillGLWindow(); //重置显示区
//MessageBox(NULL,"不能设置像素格式","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //返回FALSE
}
if (!( g_hRC=wglCreateContext(g_hDC))) //能否取得OpenGL渲染描述表?
{
KillGLWindow(); //重置显示区
//MessageBox(NULL,"不能创建OpenGL渲染描述表","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //返回FALSE
}
if(!wglMakeCurrent(g_hDC,g_hRC)) //尝试激活着色描述表
{
KillGLWindow(); //重置显示区
//MessageBox(NULL,"不能激活当前的OpenGL渲然描述表","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //返回FALSE
}
ShowWindow (g_hWnd, iCmdShow) ;
UpdateWindow ( g_hWnd) ;
if (!InitGL()) //初始化新建的GL窗口
{
KillGLWindow(); //重置显示区
//MessageBox(NULL,"初始化失败","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //返回FALSE
}
return TRUE;
}
//窗口初始化
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) //重置OpenGL窗口大小
{
if (height==0) //防止被零除
{
height=1; //将Height设为
}
glViewport(0,0,width,height); //重置当前的视口
glMatrixMode(GL_PROJECTION); // 选择投影矩阵
glLoadIdentity(); //重置投影矩阵
//设置视口的大小
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW); //选择模型观察矩阵
glLoadIdentity(); //重置模型观察矩阵
}
int InitGL(GLvoid) //此处开始对OpenGL进行所有设置
{
glShadeModel(GL_SMOOTH); //启用阴影平滑
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //黑色背景
glClearDepth(1.0f); //设置深度缓存
glEnable(GL_DEPTH_TEST); //启用深度测试
glDepthFunc(GL_LEQUAL); //所作深度测试的类型
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 告诉系统对透视进行修正
return TRUE; //初始化OK
}
int DrawGLScene(GLvoid) //从这里开始进行所有的绘制
{
static GLfloat rTri= 0.0f;
static GLfloat rQuad= 1.0f;
//////RRT(45,60)
float t= pow( 3, 0.5);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
glLoadIdentity(); //重置当前的模型观察矩阵
gluLookAt( t* 3, 2.0f, 3.0f, 0.0f, 8.0f, 0.0f, 0.0f, 1.0f, 0.0f);
double adModelMatrix[ 10][ 16];
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix[ 0]);//模視矩陣
glLoadIdentity(); //重置当前的模型观察矩阵
glRotatef(-45,1.0f,0.0f,0.0f);//绕y轴旋转三角形
glRotatef(-60,0.0f,1.0f,0.0f);//绕y轴旋转三角形
glTranslatef(-t* 3,-2.0,-3.0); //左移1.5单位,并移入屏幕6.0
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix[ 1]);//模視矩陣
///////////////////////////////////////////////////////////////////////////////
////RRT(任意角度,R表示旋转glRotate,T表示平移glTranslate)
Vec3 ptEye, ptRef, ptDir;
//视点和参考点任意(取随机数)
ptEye.x= ( rand()% 1000+ 1)/ 10;
ptEye.y= ( rand()% 1000+ 1)/ 10;
ptEye.z= ( rand()% 1000+ 1)/ 10;
ptRef.x= ( rand()% 1000+ 1)/ 10;
ptRef.y= ( rand()% 1000+ 1)/ 10;
ptRef.z= ( rand()% 1000+ 1)/ 10;
ptDir.x= ptRef.x- ptEye.x;
ptDir.y= ptRef.y- ptEye.y;
ptDir.z= ptRef.z- ptEye.z;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
glLoadIdentity(); //重置当前的模型观察矩阵
gluLookAt( ptEye.x, ptEye.y, ptEye.z, ptRef.x, ptRef.y, ptRef.z, 0, 1.0f, 0.0f);
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix[ 0]);//获取模視矩陣
glLoadIdentity(); //重置当前的模型观察矩阵
constdouble PI= 3.14159265358979;
double a1= 90- acos( ptDir.y/ sqrt(ptDir.z* ptDir.z+ ptDir.x* ptDir.x+ ptDir.y* ptDir.y))
* 180/ PI;//竖直角度
double dx= -ptDir.x, dz= -ptDir.z;
double a2= acos( dz/ sqrt( dz* dz+ dx* dx)) * 180/ PI;//水平角度
if ( dx < 0)
{
a2= -a2;
}
glRotatef(-a1,1.0f,0.0f,0.0f);//绕x轴旋转-a1,如-45度
glRotatef(-a2,0.0f,1.0f,0.0f);//绕y轴旋转-a2,如-60度
glTranslatef(-ptEye.x, -ptEye.y, -ptEye.z);//反向平移
glGetDoublev( GL_MODELVIEW_MATRIX, adModelMatrix[ 1]);//模視矩陣
for (int i= 0; i< 16; ++i)
{
if ( abs(adModelMatrix[ 1][ i] - adModelMatrix[ 0][ i] ) >= 1e-4)
{
float t= 0;
++t;
}
}
//经验证,gluLookAt的模视矩阵确实能由glTranslate,glRotate实现,glRotate旋转时对角度有误差,
//但不是很大,一般可在e-4内.
/***********************************新添的代码*******************************************/
//glTranslatef(-1.5f,0.0f,-6.0f); //左移1.5单位,并移入屏幕6.0
glTranslatef(4.0,2.0,3.0); //左移1.5单位,并移入屏幕6.0
glBegin(GL_TRIANGLES); //绘制三角形
//前侧面
glColor3f( 1.0f, 0.0f, 0.0f);//上顶点为红色
glVertex3f( 0.0f, 1.0f, 0.0f); //上顶点
glColor3f( 0.0f, 1.0f, 0.0f);//左下为绿色
glVertex3f(-1.0f,-1.0f, 1.0f); //左下
glColor3f ( 0.0f, 0.0f, 1.0f);//右下为蓝色
glVertex3f( 1.0f,-1.0f, 1.0f); //右下
//前侧面/ `
//右侧面
glColor3f( 1.0f, 0.0f, 0.0f);//上顶点为红色
glVertex3f( 0.0f, 1.0f, 0.0f); //上顶点
glColor3f ( 0.0f, 0.0f, 1.0f);//右下为蓝色
glVertex3f( 1.0f,-1.0f, 1.0f); //右下
glColor3f( 0.0f, 1.0f, 0.0f);//左下为绿色
glVertex3f( 1.0f,-1.0f, -1.0f); //左下
//右侧面/
//后侧面
glColor3f( 1.0f, 0.0f, 0.0f);//上顶点为红色
glVertex3f( 0.0f, 1.0f, 0.0f); //上顶点
glColor3f( 0.0f, 1.0f, 0.0f);//左下为绿色
glVertex3f( 1.0f,-1.0f, -1.0f); //左下
glColor3f ( 0.0f, 0.0f, 1.0f);//右下为蓝色
glVertex3f( -1.0f,-1.0f, -1.0f); //右下
//后侧面/
//左侧面
glColor3f( 1.0f, 0.0f, 0.0f);//上顶点为红色
glVertex3f( 0.0f, 1.0f, 0.0f); //上顶点
glColor3f ( 0.0f, 0.0f, 1.0f);//右下为蓝色
glVertex3f( -1.0f,-1.0f, -1.0f); //右下
glColor3f( 0.0f, 1.0f, 0.0f);//左下为绿色
glVertex3f( -1.0f,-1.0f, 1.0f); //左下
//左侧面/
glEnd(); //三角形绘制结束
//底面:正方形(要放在三角形绘制外面,否则出现错误)
glBegin( GL_QUADS);//正方形
glColor3f( 0.0f, 1.0f, 0.0f);//左下为绿色
glVertex3f( -1.0f,-1.0f, 1.0f); //左下
glColor3f ( 0.0f, 0.0f, 1.0f);//右下为蓝色
glVertex3f( 1.0f,-1.0f, 1.0f); //右下
glColor3f( 0.0f, 1.0f, 0.0f);//左下为绿色
glVertex3f( 1.0f,-1.0f, -1.0f); //左下
glColor3f ( 0.0f, 0.0f, 1.0f);//右下为蓝色
glVertex3f( -1.0f,-1.0f, -1.0f); //右下
glEnd();
//底面:正方形/
glBegin( GL_POINTS );
glVertex3d( 1.5f, 0.0f, 0.0f); //绘制原点坐标
glEnd();
glLoadIdentity();//重置当前的模型观察矩阵(此时坐标归零,旋转无用)
// 绘制正方形
glTranslatef( 1.5f,0.0f, -6.0f); // 右移单位
glRotatef( rQuad, 0.0f, 1.0f, 1.0f);//旋转
glBegin(GL_QUADS); // 绘制正方形
//glVertex3f(-1.0f, 1.0f, 0.0f); //左上
//glVertex3f( 1.0f, 1.0f, 0.0f); //右上
//glVertex3f( 1.0f,-1.0f, 0.0f); //左下
//glVertex3f(-1.0f,-1.0f, 0.0f); //右下
//顶面(逆时针)
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f( -1.0f, 1.0f, -1.0f);
glVertex3f( -1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
//顶面/
//底面(逆时针)
glColor3f( 1.0f, 0.5f, 0.0f);//橙色
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f( -1.0f, -1.0f, 1.0f);
glVertex3f( -1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
//底面/
//前面(逆时针)
glColor3f( 1.0f, 0.0f, 0.0f);//红色
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( -1.0f, 1.0f, 1.0f);
glVertex3f( -1.0f, -1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
//前面/
//后面(逆时针)
glColor3f( 1.0f, 1.0f, 0.0f);//黄色
glVertex3f( 1.0f, -1.0f, -1.0f);
glVertex3f( -1.0f, -1.0f, -1.0f);
glVertex3f( -1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
//后面/
//左侧面(逆时针)
glColor3f( 0.0f, 0.6f, 0.7f);//黄色
glVertex3f( -1.0f, 1.0f, 1.0f);
glVertex3f( -1.0f, 1.0f, -1.0f);
glVertex3f( -1.0f, -1.0f, -1.0f);
glVertex3f( -1.0f, -1.0f, 1.0f);
//左侧面/
//右侧面(逆时针)
glColor3f( 1.0f, 0.0f, 1.0f);//紫罗兰
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
//右侧面/
glEnd(); //正方形绘制结束
// 绘制正方形/
/****************************************************************************************/
/*rTri += 0.2f;*/
rTri += 1.2f;
rQuad += 1.15f;
//rQuad += 0.15f;
return TRUE; //一切OK
}
GLvoid KillGLWindow(GLvoid) //正常销毁窗口
{
if (g_hRC) //我们拥有OpenGL描述表吗?
{
if (!wglMakeCurrent(NULL,NULL)) //我们能否释放DC和RC描述表?
{
MessageBox(NULL,TEXT("释放DC或RC失败。"),TEXT("关闭错误"),MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(g_hRC)) //我们能否删除RC?
{
MessageBox(NULL,TEXT("释放RC失败。"),TEXT("关闭错误"),MB_OK | MB_ICONINFORMATION);
}
g_hRC=NULL; //将RC设为NULL
}
if (g_hDC && !ReleaseDC(g_hWnd,g_hDC)) //我们能否释放DC?
{
MessageBox(NULL,TEXT("释放DC失败。"),TEXT("关闭错误"),MB_OK | MB_ICONINFORMATION);
g_hDC= NULL; //将DC设为NULL
}
if (g_hWnd && !DestroyWindow(g_hWnd)) //能否销毁窗口?
{
//MessageBox(NULL,"释放窗口句柄失败。","关闭错误",MB_OK | MB_ICONINFORMATION);
g_hWnd=NULL; //将g_hWnd设为NULL
}
if (!UnregisterClass( szAppName ,g_hInstance)) //能否注销类?
{
//MessageBox(NULL,"不能注销窗口类。","关闭错误",MB_OK | MB_ICONINFORMATION);
g_hInstance =NULL; //将hInstance设为NULL
}
}