OpenGL_10 3D空间中移动图像

在本节中,并没有新的知识,只是通过glTranslatef()和glRotatef()函数对星星纹理映射的矩形的位置变换以及glColor4ub()函数对星星颜色改变达到混合效果实现星星自转、绕z轴旋转和闪烁的效果,可以说是对之前知识的回顾。

NEHEOpenGL例程的代码注释率达到90%以上,而51CTO翻译版的NEHE教程注释较少,上午又下载了NEHE例程源码,并将其英文注释全部翻译了一遍,算是对知识的一次巩固。

NeHe原版例程下载地址OpenGL教程Nehe版下载地址


#include "OpenGLSB.h"
#include <stdio.h>
#include <stdlib.h>         
#include "glaux.h"

HDC			hDC = NULL;		// 私有GDI设备环境
HGLRC		hRC = NULL;		// 永久透视环境
HWND		hWnd = NULL;	// 保持窗口句柄
HINSTANCE	hInstance;		// 保持应用实例

bool	keys[256];			// 键盘活动存储数组
bool	active = TRUE;		// 窗口有效标志默认设置为TRUE
bool	fullscreen = TRUE;	// 全屏标志默认设置为全屏

bool	twinkle;			// 闪烁的星星
bool	tp;					// ‘T’键按下?

const int	num = 50;		//要画的星星的个数

typedef struct				// 创建一个星星的结构体
{
	int r, g, b;			// 星星的颜色
	GLfloat dist,			// 星星距离中心的距离
		angle;			    // 星星当前角度
}
stars;
stars star[num];			// 需要保持对'num'个星星的追踪

GLfloat	zoom = -15.0f;		// 星星远离的距离
GLfloat tilt = 90.0f;		// 视角倾斜
GLfloat	spin;				// 旋转星星

GLuint	loop;				// 通用循环变量
GLuint	texture[1];			// 用于存储一个纹理

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);	// WndProc函数声明

AUX_RGBImageRec *LoadBMP(char *Filename)                // 载入一个位图图像
{
	FILE *File = NULL;                                //文件句柄

	if (!Filename)                                  // 确保文件名给出
	{
		return NULL;                            // 如果不返回NULL
	}

	File = fopen(Filename, "r");                    // 检查文件是否存在

	if (File)                                       //文件存在否? 
	{
		fclose(File);                           // 关闭句柄
		return auxDIBImageLoad(Filename);       // 加载位图并返回一个指针
	}
	return NULL;                                    // 如果加载失败则返回NULL
}

int LoadGLTextures()                                    //加载位图并转换成纹理
{
	int Status = FALSE;                               //状态指示器

	AUX_RGBImageRec *TextureImage[1];               // 创建一个纹理存储空间

	memset(TextureImage, 0, sizeof(void *) * 1);        // 将指针指向NULL

														// 加载位图,检查错误,如果文图不存在则退出
	if (TextureImage[0] = LoadBMP("Data/Star.bmp"))
	{
		Status = TRUE;                            // 将状态设置未TRUE

		glGenTextures(1, &texture[0]);          // 创建一个纹理

												// 创建线性滤波纹理
		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
	}
	if (TextureImage[0])                            // 如果纹理存在
	{
		if (TextureImage[0]->data)              // 如果纹理图像存在
		{
			free(TextureImage[0]->data);    // 释放纹理图像内存
		}

		free(TextureImage[0]);                  // 释放图像结构体
	}

	return Status;                                  // 返回状态
}

GLvoid ReSizeGLScene(GLsizei width, GLsizei height)		// 调整GL窗口并初始化
{
	if (height == 0)										// 避免A被0除
	{
		height = 1;										// 让高度等于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的所有设置
{
	if (!LoadGLTextures())								//调用纹理加载历程
	{
		return FALSE;									// 如果纹理未能加载返回FALSE
	}

	glEnable(GL_TEXTURE_2D);							// 启用纹理映射
	glShadeModel(GL_SMOOTH);							// 启用平滑阴影
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// 黑色背景
	glClearDepth(1.0f);									// 深度缓存启动
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// 真实的nice级的场景计算
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);					// 设置半透明混合函数
	glEnable(GL_BLEND);

	for (loop = 0; loop<num; loop++)
	{
		star[loop].angle = 0.0f;
		star[loop].dist = (float(loop) / num)*5.0f;
		star[loop].r = rand() % 256;
		star[loop].g = rand() % 256;
		star[loop].b = rand() % 256;
	}
	return TRUE;										// 初始化OK

int DrawGLScene(GLvoid)									// 这儿是我们所做的所有的绘制
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// 清除屏幕和深度缓存
	glBindTexture(GL_TEXTURE_2D, texture[0]);			// 选择我们的纹理

	for (loop = 0; loop<num; loop++)						// 循环所有的星星
	{
		glLoadIdentity();								// 我们画每个星星前重置模型观察矩阵
		glTranslatef(0.0f, 0.0f, zoom);					// 深入屏幕(使用zoom里的数值)
		glRotatef(tilt, 1.0f, 0.0f, 0.0f);					//倾斜视角(使用tilt里的数值)
		glRotatef(star[loop].angle, 0.0f, 1.0f, 0.0f);		// 旋转至当前星星的角度
		glTranslatef(star[loop].dist, 0.0f, 0.0f);		// 在x平面前移(注:在观察者视角应是向右移动)
		glRotatef(-star[loop].angle, 0.0f, 1.0f, 0.0f);	// 取消当前星星角度
		glRotatef(-tilt, 1.0f, 0.0f, 0.0f);				// 取消屏幕倾斜

		if (twinkle)									//启用闪烁效果
		{
			//第一遍绘制静止的星星
			glColor4ub(star[(num - loop) - 1].r, star[(num - loop) - 1].g, star[(num - loop) - 1].b, 255);
			glBegin(GL_QUADS);							//开始绘制纹理映射的四边形
			glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f);
			glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f);
			glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
			glEnd();
		}
		//第二遍绘制绕z轴旋转的星星
		glRotatef(spin, 0.0f, 0.0f, 1.0f);
		glColor4ub(star[loop].r, star[loop].g, star[loop].b, 255);
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
		glEnd();

		spin += 0.01f;
		star[loop].angle += float(loop) / num;
		star[loop].dist -= 0.01f;
		if (star[loop].dist<0.0f)				//星星到达中心
		{
			star[loop].dist += 5.0f;
			star[loop].r = rand() % 256;
			star[loop].g = rand() % 256;
			star[loop].b = rand() % 256;
		}
	}
	return TRUE;										//一切OK
}

GLvoid KillGLWindow(GLvoid)								//恰当地消除窗口
{
	if (fullscreen)										//我们在全屏模式吗
	{
		ChangeDisplaySettings(NULL, 0);					// 如果是,我们切换回桌面
		ShowCursor(TRUE);								// 显示鼠标指针
	}

	if (hRC)											//我们拥有一个渲染描述表吗
	{
		if (!wglMakeCurrent(NULL, NULL))					// 我们能释放DC(设备描述表)或RC(渲染描述表)吗
		{
			MessageBox(NULL, "Release Of DC And RC Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);//消息盒显示错误
		}

		if (!wglDeleteContext(hRC))						// 我们能删除渲染描述表吗
		{
			MessageBox(NULL, "Release Rendering Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
		}
		hRC = NULL;										// 将hRC设置为空
	}

	if (hDC && !ReleaseDC(hWnd, hDC))					// 我们能释放设备描述表吗
	{
		MessageBox(NULL, "Release Device Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
		hDC = NULL;										// 将DC设置为空
	}

	if (hWnd && !DestroyWindow(hWnd))					// 我们能清除窗口吗
	{
		MessageBox(NULL, "Could Not Release hWnd.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
		hWnd = NULL;										// 将窗口句柄设置为空
	}

	if (!UnregisterClass("OpenGL", hInstance))			// 我们能注销类吗
	{
		MessageBox(NULL, "Could Not Unregister Class.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
		hInstance = NULL;									// 将实例句柄设置为空
	}
}

/*	This Code Creates Our OpenGL Window.  Parameters Are:					*
*	title			- Title To Appear At The Top Of The Window				*
*	width			- Width Of The GL Window Or Fullscreen Mode				*
*	height			- Height Of The GL Window Or Fullscreen Mode			*
*	bits			- Number Of Bits To Use For Color (8/16/24/32)			*
*	fullscreenflag	- Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)	*/

/*	这段代码创建我们地OpenGL窗口,参数为:									*
*	title			-标题出现在窗口地最顶端									*
*	width			-GL窗口或全屏模式的宽度									*
*	height			-GL窗口或全屏模式的高度									*
*	bits			-用于表示颜色的位数(8/16/24/32)							*
*	fullscreenflag	-使用全屏模式(TRUE)或窗口模式(FLASE)					*
*/

BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
	GLuint		PixelFormat;			// 搜索到匹配项后保持结果
	WNDCLASS	wc;						// Windows类结构
	DWORD		dwExStyle;				// 窗口拓展类型
	DWORD		dwStyle;				// 窗口类型
	RECT		WindowRect;				// 取得矩形左上/右下值
	WindowRect.left = (long)0;			// 左值设为0
	WindowRect.right = (long)width;		// 右值设置为要求的宽度
	WindowRect.top = (long)0;				// 上值设置为0
	WindowRect.bottom = (long)height;		// 下值设置为要求的高度

	fullscreen = fullscreenflag;			//设置全局全屏标志

	hInstance = GetModuleHandle(NULL);				//取得我们窗口的一个实例
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// 重绘尺寸和窗口自己的设备描述表
	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 = "OpenGL";								//设置类的名称

	if (!RegisterClass(&wc))									//试图注册窗口类
	{
		MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;											//返回FALSE
	}

	if (fullscreen)												//试图全屏模式?
	{
		DEVMODE dmScreenSettings;								// 设备模式
		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));	//确保内存清空
		dmScreenSettings.dmSize = sizeof(dmScreenSettings);		//开发模式结构体大小
		dmScreenSettings.dmPelsWidth = width;				// 选定屏幕宽度
		dmScreenSettings.dmPelsHeight = height;				// 选定屏幕高度
		dmScreenSettings.dmBitsPerPel = bits;					// 选定像素色彩深度
		dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		// 试图设置选定的模式并返回结果  提示: CDS_FULLSCREEN没有Start Bar.
		if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
		{
			// 如果模式失败,提供两个选项,退出或使用窗口模式
			if (MessageBox(NULL, "The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?", "NeHe GL", MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
			{
				fullscreen = FALSE;		//选定窗口模式.  Fullscreen = FALSE
			}
			else
			{
				//弹出一个消息盒让用户知道程序正在关闭
				MessageBox(NULL, "Program Will Now Close.", "ERROR", 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,							// 窗口拓展类型
		"OpenGL",							// 类名
		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, "Window Creation Error.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;								//返回FALSE
	}

	static	PIXELFORMATDESCRIPTOR pfd =				// pfd(像素格式描述符)告诉窗口我们想让东西变成什么样
	{
		sizeof(PIXELFORMATDESCRIPTOR),				// pfd大小
		1,											// 版本号
		PFD_DRAW_TO_WINDOW |						// 格式必须支持窗口
		PFD_SUPPORT_OPENGL |						// 格式必须支持OpenGL
		PFD_DOUBLEBUFFER,							// 必须支持双缓存
		PFD_TYPE_RGBA,								// 需要RGBA格式(红绿蓝Alpha)
		bits,										// 选择我们的色彩深度
		0, 0, 0, 0, 0, 0,							// 忽略的色彩位
		0,											// 无Alpha缓存
		0,											// 忽略的移位
		0,											// 无累加缓存
		0, 0, 0, 0,									// 忽略的累加缓存
		16,											// 16Bit Z-Buffer (深度缓存)  
		0,											// 无蒙版缓存
		0,											// 无辅助缓存
		PFD_MAIN_PLANE,								// 主绘图层
		0,											// 预留项
		0, 0, 0										// 忽略层遮罩
	};

	if (!(hDC = GetDC(hWnd)))							//我们获得设备描述表了吗?
	{
		KillGLWindow();								// 重置显示
		MessageBox(NULL, "Can't Create A GL Device Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;								// 返回错误
	}

	if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd)))	// windows找到匹配的像素格式了吗?
	{
		KillGLWindow();								// 重置显示
		MessageBox(NULL, "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;								// 返回FALSE
	}

	if (!SetPixelFormat(hDC, PixelFormat, &pfd))		//我们能设置像素格式吗?
	{
		KillGLWindow();								//重置显示
		MessageBox(NULL, "Can't Set The PixelFormat.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;								// 返回 FALSE
	}

	if (!(hRC = wglCreateContext(hDC)))				//我们能获取渲染描述表吗?
	{
		KillGLWindow();								// 重置显示
		MessageBox(NULL, "Can't Create A GL Rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;								//返回 FALSE
	}

	if (!wglMakeCurrent(hDC, hRC))					// 试图激活像素描述表
	{
		KillGLWindow();								// R重置显示
		MessageBox(NULL, "Can't Activate The GL Rendering Context.", "ERROR", 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, "Initialization Failed.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;								// 返回 FALSE
	}

	return TRUE;									// 成功
}

LRESULT CALLBACK WndProc(HWND	hWnd,			// 解决这个窗口
	UINT	uMsg,			// 这个窗口的消息
	WPARAM	wParam,			// 额外消息信息
	LPARAM	lParam)			// 额外消息信息
{
	switch (uMsg)									//检查消息
	{
	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:								// 我们收到关闭消息?
	{
		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;									//窗口消息结构
	BOOL	done = FALSE;								// Bool变量用于退出循环

														//询问用户他们想要哪种模式
	if (MessageBox(NULL, "Would You Like To Run In Fullscreen Mode?", "Start FullScreen?", MB_YESNO | MB_ICONQUESTION) == IDNO)
	{
		fullscreen = FALSE;							// 窗口模式
	}

	// 创建我们的OpenGL窗口
	if (!CreateGLWindow("NeHe's Animated Blended Textures Tutorial", 640, 480, 16, fullscreen))
	{
		return 0;									// 窗口未被创建退出
	}

	while (!done)									// 当done=FALSE循环
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))	//有消息等待?
		{
			if (msg.message == WM_QUIT)				//我们收到一个退出的消息?
			{
				done = TRUE;							//如果是done=TRUE
			}
			else									// 如果不是,处理窗口消息
			{
				TranslateMessage(&msg);				// 翻译消息
				DispatchMessage(&msg);				// 传递消息
			}
		}
		else										//没有消息
		{
			//绘制场景.监视来自DrawGLScene()的ESC键和退出消息
			if ((active && !DrawGLScene()) || keys[VK_ESCAPE])	// 激活? 或 有一个退出收到?
			{
				done = TRUE;							// ESC or DrawGLScene标记一个退出
			}
			else									// 未退出,更行场景   (原例程标注为not time to quit)
			{
				SwapBuffers(hDC);					//转换缓存(双缓存)
				if (keys['T'] && !tp)
				{
					tp = TRUE;
					twinkle = !twinkle;
				}
				if (!keys['T'])
				{
					tp = FALSE;
				}

				if (keys[VK_UP])
				{
					tilt -= 0.5f;
				}

				if (keys[VK_DOWN])
				{
					tilt += 0.5f;
				}

				if (keys[VK_PRIOR])
				{
					zoom -= 0.2f;
				}

				if (keys[VK_NEXT])
				{
					zoom += 0.2f;
				}

				if (keys[VK_F1])						//F1按下?
				{
					keys[VK_F1] = FALSE;					//如果是,让Key为FALSE
					KillGLWindow();						// 关闭当前窗口
					fullscreen = !fullscreen;				// 开启 全屏 / 窗口模式
															// 重新创建我们的OpenGL窗口
					if (!CreateGLWindow("NeHe's Animated Blended Textures Tutorial", 640, 480, 16, fullscreen))
					{
						return 0;						// 如果窗口没有被创建退出
					}
				}
			}
		}
	}

	// 关闭
	KillGLWindow();									//关闭窗口
	return (msg.wParam);							// 退出程序
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值