服务器之家

服务器之家 > 正文

C++ Opengl旋转功能附源码下载

时间:2021-10-06 11:54     来源/作者:it_xiangqiang

项目开发环境:
开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1,三方库:OpenGL。

项目功能:
是图形旋转起来。

项目源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
/*********************包含链接的库文件*******************************************************************************************/
#pragma comment( lib, "opengl32.lib" )             
#pragma comment( lib, "glu32.lib" )
#pragma comment( lib, "glut32.lib")
#pragma comment( lib, "glew32.lib")
#pragma comment( lib, "glaux.lib")
#pragma comment( lib, "vfw32.lib" )
/********************************************************************************************************************************/
 
#include <windows.h>      // Windows的头文件
#include "glew.h"           // 包含最新的gl.h,glu.h库
#include "glut.h"           // 包含OpenGL实用库
 
HDC         hDC = NULL;     // 窗口着色描述表句柄
HGLRC       hRC = NULL;     // OpenGL渲染描述表句柄
HWND        hWnd = NULL;        // 保存我们的窗口句柄
HINSTANCE   hInstance;      // 保存程序的实例
 
bool    keys[256];          // 保存键盘按键的数组
bool    active = TRUE;      // 窗口的活动标志,缺省为TRUE
bool    fullscreen = TRUE;  // 全屏标志缺省,缺省设定成全屏模式
/***********************************新添的代码***********************************************************************************/
GLfloat     rtri;           // 用于三角形的角度
GLfloat     rquad;          // 用于四边形的角度
/********************************************************************************************************************************/
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);   // WndProc的定义
 
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)     // 重置OpenGL窗口大小
{
    if (height == 0)                                        // 防止被零除
    {
        height = 1;                                     // 将Height设为1
    }
 
    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)                                 // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
    glLoadIdentity();                                   // 重置当前的模型观察矩阵
    glTranslatef(-1.5f, 0.0f, -6.0f);                       // 左移 1.5 单位,并移入屏幕 6.0
/***********************************新添的代码***********************************************************************************/
    glRotatef(rtri, 0.0f, 1.0f, 0.0f);                      // 绕Y轴旋转三角形
/********************************************************************************************************************************/
    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, 0.0f);                 // 左下
    glColor3f(0.0f, 0.0f, 1.0f);                        // 设置当前色为蓝色
    glVertex3f(1.0f, -1.0f, 0.0f);                  // 右下
    glEnd();                                            // 三角形绘制结束
    glTranslatef(3.0f, 0.0f, 0.0f);                     // 右移3单位
 
    glColor3f(0.5f, 0.5f, 1.0f);                            // 一次性将当前色设置为蓝色
/***********************************新添的代码***********************************************************************************/
    glLoadIdentity();                                   // 重置模型观察矩阵
    glTranslatef(1.5f, 0.0f, -6.0f);                        // 右移1.5单位,并移入屏幕 6.0
    glRotatef(rquad, 1.0f, 0.0f, 0.0f);                 // 绕X轴旋转四边形
    rtri += 0.2f;                                           // 增加三角形的旋转变量
    rquad -= 0.15f;                                     // 减少四边形的旋转变量
/********************************************************************************************************************************/
    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);                 // 右下
    glEnd();                                            // 正方形绘制结束
    return TRUE;                                        // 一切 OK
}
 
GLvoid KillGLWindow(GLvoid)                             // 正常销毁窗口
{
    if (fullscreen)                                     // 我们处于全屏模式吗?
    {
        ChangeDisplaySettings(NULL, 0);                 // 是的话,切换回桌面
        ShowCursor(TRUE);                               // 显示鼠标指针
    }
 
    if (hRC)                                            //我们拥有OpenGL描述表吗?
    {
        if (!wglMakeCurrent(NULL, NULL))                    // 我们能否释放DC和RC描述表?
        {
            MessageBox(NULL, "释放DC或RC失败。", "关闭错误", MB_OK | MB_ICONINFORMATION);
        }
 
        if (!wglDeleteContext(hRC))                     // 我们能否删除RC?
        {
            MessageBox(NULL, "释放RC失败。", "关闭错误", MB_OK | MB_ICONINFORMATION);
        }
        hRC = NULL;                                     // 将RC设为 NULL
    }
 
    if (hDC && !ReleaseDC(hWnd, hDC))                   // 我们能否释放 DC?
    {
        MessageBox(NULL, "释放DC失败。", "关闭错误", MB_OK | MB_ICONINFORMATION);
        hDC = NULL;                                     // 将 DC 设为 NULL
    }
 
    if (hWnd && !DestroyWindow(hWnd))                   // 能否销毁窗口?
    {
        MessageBox(NULL, "释放窗口句柄失败。", "关闭错误", MB_OK | MB_ICONINFORMATION);
        hWnd = NULL;                                        // 将 hWnd 设为 NULL
    }
 
    if (!UnregisterClass("OpenG", hInstance))           // 能否注销类?
    {
        MessageBox(NULL, "不能注销窗口类。", "关闭错误", MB_OK | MB_ICONINFORMATION);
        hInstance = NULL;                                   // 将 hInstance 设为 NULL
    }
}
 
/*  这个函数创建我们OpenGL窗口,参数为:                                   *
 *  title           - 窗口标题                                              *
 *  width           - 窗口宽度                                              *
 *  height          - 窗口高度                                              *
 *  bits            - 颜色的位深(8/16/32)                                    *
 *  fullscreenflag  - 是否使用全屏模式,全屏模式(TRUE),窗口模式(FALSE)       */
 
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
    GLuint      PixelFormat;            // 保存查找匹配的结果
    WNDCLASS    wc;                     // 窗口类结构
    DWORD       dwExStyle;              // 扩展窗口风格
    DWORD       dwStyle;                // 窗口风格
    RECT        WindowRect;             // 取得矩形的左上角和右下角的坐标值
    WindowRect.left = (long)0;          // 将Left  设为 0
    WindowRect.right = (long)width;     // 将Right 设为要求的宽度
    WindowRect.top = (long)0;               // 将Top  设为 0
    WindowRect.bottom = (long)height;       // 将Bottom 设为要求的高度
 
    fullscreen = fullscreenflag;            // 设置全局全屏标志
 
    hInstance = GetModuleHandle(NULL);              // 取得我们窗口的实例
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;  // 移动时重画,并为窗口取得DC
    wc.lpfnWndProc = (WNDPROC)WndProc;                  // WndProc处理消息
    wc.cbClsExtra = 0;                                  // 无额外窗口数据
    wc.cbWndExtra = 0;                                  // 无额外窗口数据
    wc.hInstance = hInstance;                           // 设置实例
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);         // 装入缺省图标
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);           // 装入鼠标指针
    wc.hbrBackground = NULL;                                    // GL不需要背景
    wc.lpszMenuName = NULL;                                 // 不需要菜单
    wc.lpszClassName = "OpenG";                         // 设定类名字
 
    if (!RegisterClass(&wc))                                    // 尝试注册窗口类
    {
        MessageBox(NULL, "注册窗口失败", "错误", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                                           // 退出并返回FALSE
    }
 
    if (fullscreen)                                             // 要尝试全屏模式吗?
    {
        DEVMODE dmScreenSettings;                               // 设备模式
        memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); // 确保内存清空为零
        dmScreenSettings.dmSize = sizeof(dmScreenSettings);     // Devmode 结构的大小
        dmScreenSettings.dmPelsWidth = width;               // 所选屏幕宽度
        dmScreenSettings.dmPelsHeight = height;             // 所选屏幕高度
        dmScreenSettings.dmBitsPerPel = bits;                   // 每象素所选的色彩深度
        dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
 
        // 尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态条
        if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
        {
            // 若模式失败,提供两个选项:退出或在窗口内运行。
            if (MessageBox(NULL, "全屏模式在当前显卡上设置失败!\n使用窗口模式?", "XQ G", MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
            {
                //如果用户选择窗口模式,变量fullscreen 的值变为FALSE,程序继续运行
                fullscreen = FALSE;     // 选择窗口模式(Fullscreen=FALSE)
            }
            else
            {
                //如果用户选择退出,弹出消息窗口告知用户程序将结束。并返回FALSE告诉程序窗口未能成功创建。程序退出。
                MessageBox(NULL, "程序将被关闭", "错误", MB_OK | MB_ICONSTOP);
                return FALSE;                                   // 退出并返回 FALSE
            }
        }
    }
 
    if (fullscreen)                                             // 仍处于全屏模式吗?
    {
        dwExStyle = WS_EX_APPWINDOW;                                // 扩展窗体风格
        dwStyle = WS_POPUP;                                     // 窗体风格
        ShowCursor(FALSE);                                      // 隐藏鼠标指针
    }
    else
    {
        dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;         // 扩展窗体风格
        dwStyle = WS_OVERLAPPEDWINDOW;                          // 窗体风格
    }
 
    AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);     // 调整窗口达到真正要求的大小
 
    // 创建窗口
    if (!(hWnd = CreateWindowEx(dwExStyle,                          // 扩展窗体风格
        "OpenG",                            // 类名字
        title,                              // 窗口标题
        dwStyle |                           // 必须的窗体风格属性
        WS_CLIPSIBLINGS |                   // 必须的窗体风格属性
        WS_CLIPCHILDREN,                    // 必须的窗体风格属性
        0, 0,                               // 窗口位置
        WindowRect.right - WindowRect.left, // 计算调整好的窗口宽度
        WindowRect.bottom - WindowRect.top, // 计算调整好的窗口高度
        NULL,                               // 无父窗口
        NULL,                               // 无菜单
        hInstance,                          // 实例
        NULL)))                             // 不向WM_CREATE传递任何东东
    {
        KillGLWindow();                             // 重置显示区
        MessageBox(NULL, "窗口创建错误", "错误", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                               // 返回 FALSE
    }
 
    static  PIXELFORMATDESCRIPTOR 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 (!(hDC = GetDC(hWnd)))                           // 取得设备描述表了么?
    {
        KillGLWindow();                             // 重置显示区
        MessageBox(NULL, "不能创建一个窗口设备描述表", "错误", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                               // 返回 FALSE
    }
 
    if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd)))  // Windows 找到相应的象素格式了吗?
    {
        KillGLWindow();                             // 重置显示区
        MessageBox(NULL, "不能创建一种相匹配的像素格式", "错误", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                               // 返回 FALSE
    }
 
    if (!SetPixelFormat(hDC, PixelFormat, &pfd))        // 能够设置象素格式么?
    {
        KillGLWindow();                             // 重置显示区
        MessageBox(NULL, "不能设置像素格式", "错误", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                               // 返回 FALSE
    }
 
    if (!(hRC = wglCreateContext(hDC)))             // 能否取得OpenGL渲染描述表?
    {
        KillGLWindow();                             // 重置显示区
        MessageBox(NULL, "不能创建OpenGL渲染描述表", "错误", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                               // 返回 FALSE
    }
 
    if (!wglMakeCurrent(hDC, hRC))                  // 尝试激活着色描述表
    {
        KillGLWindow();                             // 重置显示区
        MessageBox(NULL, "不能激活当前的OpenGL渲然描述表", "错误", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                               // 返回 FALSE
    }
 
    ShowWindow(hWnd, SW_SHOW);                      // 显示窗口
    SetForegroundWindow(hWnd);                      // 略略提高优先级
    SetFocus(hWnd);                                 // 设置键盘的焦点至此窗口
    ReSizeGLScene(width, height);                   // 设置透视 GL 屏幕
 
    if (!InitGL())                                  // 初始化新建的GL窗口
    {
        KillGLWindow();                             // 重置显示区
        MessageBox(NULL, "初始化失败", "错误", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                               // 返回 FALSE
    }
 
    return TRUE;                                    // 成功
}
 
LRESULT CALLBACK WndProc(HWND   hWnd,           // 窗口的句柄   
    UINT    uMsg,           // 窗口的消息
    WPARAM  wParam,         // 附加的消息内容
    LPARAM  lParam)         // 附加的消息内容
{
    switch (uMsg)                                   // 检查Windows消息
    {
    case WM_ACTIVATE:                           // 监视窗口激活消息
    {
        if (!HIWORD(wParam))                    // 检查最小化状态
        {
            active = TRUE;                      // 程序处于激活状态
        }
        else
        {
            active = FALSE;                     // 程序不再激活
        }
 
        return 0;                               // 返回消息循环
    }
 
    case WM_SYSCOMMAND:                         // 系统中断命令
    {
        switch (wParam)                         // 检查系统调用
        {
        case SC_SCREENSAVE:                 // 屏保要运行?
        case SC_MONITORPOWER:               // 显示器要进入节电模式?
            return 0;                           // 阻止发生
        }
        break;                                  // 退出
    }
 
    case WM_CLOSE:                              // 收到Close消息?
    {
        PostQuitMessage(0);                     // 发出退出消息
        return 0;                               // 返回
    }
 
    case WM_KEYDOWN:                            // 有键按下么?
    {
        keys[wParam] = TRUE;                    // 如果是,设为TRUE
        return 0;                               // 返回
    }
 
    case WM_KEYUP:                              // 有键放开么?
    {
        keys[wParam] = FALSE;                   // 如果是,设为FALSE
        return 0;                               // 返回
    }
 
    case WM_SIZE:                               // 调整OpenGL窗口大小
    {
        ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); // LoWord=Width,HiWord=Height
        return 0;                               // 返回
    }
    }
 
    // 向 DefWindowProc传递所有未处理的消息。
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
 
int WINAPI WinMain(HINSTANCE    hInstance,          // 当前窗口实例
    HINSTANCE   hPrevInstance,      // 前一个窗口实例
    LPSTR       lpCmdLine,          // 命令行参数
    int         nCmdShow)           // 窗口显示状态
{
    MSG     msg;                                    // Windowsx消息结构
    BOOL    done = FALSE;                               // 用来退出循环的Bool 变量
 
 
    fullscreen = FALSE;                         // FALSE为窗口模式
 
 
    // 创建OpenGL窗口
/***********************************新添的代码***********************************************************************************/
    int scrWidth, scrHeight;
    RECT rect;
    scrWidth = GetSystemMetrics(SM_CXSCREEN);
    scrHeight = GetSystemMetrics(SM_CYSCREEN);
    BOOL Res = CreateGLWindow("旋转实例", 640, 480, 16, fullscreen);
    if (Res)
    {
        GetWindowRect(hWnd, &rect);
        SetWindowPos(hWnd, HWND_TOPMOST, (scrWidth - rect.right) / 2, (scrHeight - rect.bottom) / 2, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW);
    }
    else
    {
        return 0;                       // 如果窗口未能创建,程序退出
    }
    while (!done)                                   // 保持循环直到 done=TRUE
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))   // 有消息在等待吗?
        {
            if (msg.message == WM_QUIT)             // 收到退出消息?
            {
                done = TRUE;                            // 是,则done=TRUE
            }
            else                                    // 不是,处理窗口消息
            {
                TranslateMessage(&msg);             // 翻译消息
                DispatchMessage(&msg);              // 发送消息
            }
        }
        else                                        // 如果没有消息
        {
            // 绘制场景。监视ESC键和来自DrawGLScene()的退出消息
            if (active)                             // 程序激活的么?
            {
                if (keys[VK_ESCAPE])                // ESC 按下了么?
                {
                    done = TRUE;                        // ESC 发出退出信号
                }
                else                                // 不是退出的时候,刷新屏幕
                {
                    DrawGLScene();                  // 绘制场景
                    SwapBuffers(hDC);               // 交换缓存 (双缓存)
                }
            }
 
            if (keys[VK_F1])                        // F1键按下了么?
            {
                keys[VK_F1] = FALSE;                    // 若是,使对应的Key数组中的值为 FALSE
                KillGLWindow();                     // 销毁当前的窗口
                fullscreen = !fullscreen;               // 切换 全屏 / 窗口 模式
                // 重建 OpenGL 窗口
/***********************************新添的代码***********************************************************************************/
                if (!CreateGLWindow("旋转实例", 640, 480, 16, fullscreen))
                    /********************************************************************************************************************************/
                {
                    return 0;                       // 如果窗口未能创建,程序退出
                }
            }
        }
    }
 
    // 关闭程序
    KillGLWindow();                                 // 销毁窗口
    return (msg.wParam);                            // 退出程序
}

项目演示:

C++ Opengl旋转功能附源码下载

项目完整下载地址:

链接: https://pan.baidu.com/s/1qg1PwVd78zE-QgRVMRMVow 提取码: us6g

总结

到此这篇关于C++ Opengl旋转功能附源码下载的文章就介绍到这了,更多相关C++ Opengl旋转源码内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/it_xiangqiang/article/details/109668859

标签:

相关文章

热门资讯

yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
2021年耽改剧名单 2021要播出的59部耽改剧列表
2021年耽改剧名单 2021要播出的59部耽改剧列表 2021-03-05
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
返回顶部