OpenGL

画三角形


#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
using namespace std;

const char *vertexShanderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(1.0f, 0.7f, 0.2f, 1.0f);\n"
"}\n\0";


void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
	glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}
}


int main() {
	//init
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	//glfw window
	GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
	if (window == NULL) {
		cout << "Failed to create GLFW window" << endl;
		glfwTerminate();
		return -1;
	}

	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	//glad
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		cout << "Failed to initialize GLAD" << endl;
		return -1;
	}

	//shader
	//=============

	//vertex shader
	int vertexshader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexshader, 1, &vertexShanderSource, NULL);
	glCompileShader(vertexshader);

	int success;
	char inflLog[512];
	glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(vertexshader, 512, NULL, inflLog);
		cout << "ERROR:SHADER" << endl;
	}

	//fragment shader
	int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentshader, 1, &fragmentShaderSource, NULL);
	glCompileShader(vertexshader);
	
	glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(fragmentshader, 512, NULL, inflLog);
		cout << "ERROR:SHADER" << endl;
	}

	//link shanders
	int shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vertexshader);
	glAttachShader(shaderProgram, fragmentshader);
	glLinkProgram(shaderProgram);

	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, inflLog);
		cout << "ERROR:SHADER" << endl;
	}
	glDeleteShader(vertexshader);
	glDeleteShader(fragmentshader);

	//vertex data
	float vertices[] = {
		-0.5f, -0.5f, 0.0f,
		0.5f, -0.5f, 0.0f,
		0.0f, 0.5f, 0.0f
	};

	unsigned int VAO, VBO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);

	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	while (!glfwWindowShouldClose(window)) {
		processInput(window);

		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		glUseProgram(shaderProgram);
		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 3);

		glfwSwapBuffers(window);
		glfwPollEvents();

	}

	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);

	glfwTerminate();
	return 0;

}

画球体

#include<stdio.h>

// include glut & glew

#include<GL/glut.h>

#include <GLFW/glfw3.h>
#include <iostream>
using namespace std;



#define PI 3.14159265358979323846
#define PI2 6.28318530717958647692

GLsizei width = 600, height = 600;

int uStepsNum = 50, vStepNum = 50;



class Point
{
public:
	Point() {};
	Point(double a, double b, double c) :x(a), y(b), z(c) {};
public:
	double x;
	double y;
	double z;
};


Point getPoint(double u, double v)
{
	double x = sin(PI*v)*cos(PI2*u);
	double y = sin(PI*v)*sin(PI2*u);
	double z = cos(PI*v);
	return Point(x, y, z);
}


void drawWire()
{
	double ustep = 1 / (double)uStepsNum, vstep = 1 / (double)vStepNum;
	double u = 0, v = 0;
	//绘制下端三角形组
	for (int i = 0; i < uStepsNum; i++)
	{
		glBegin(GL_LINE_LOOP);
		Point a = getPoint(0, 0);
		glVertex3d(a.x, a.y, a.z);
		Point b = getPoint(u, vstep);
		glVertex3d(b.x, b.y, b.z);
		Point c = getPoint(u + ustep, vstep);
		glVertex3d(c.x, c.y, c.z);
		u += ustep;
		glEnd();
	}
	//绘制中间四边形组
	u = 0, v = vstep;
	for (int i = 1; i < vStepNum - 1; i++)
	{
		for (int j = 0; j < uStepsNum; j++)
		{
			glBegin(GL_LINE_LOOP);
			Point a = getPoint(u, v);
			Point b = getPoint(u + ustep, v);
			Point c = getPoint(u + ustep, v + vstep);
			Point d = getPoint(u, v + vstep);
			glVertex3d(a.x, a.y, a.z);
			glVertex3d(b.x, b.y, b.z);
			glVertex3d(c.x, c.y, c.z);
			glVertex3d(d.x, d.y, d.z);
			u += ustep;
			glEnd();
		}
		v += vstep;
	}
	//绘制下端三角形组
	u = 0;
	for (int i = 0; i < uStepsNum; i++)
	{
		glBegin(GL_LINE_LOOP);
		Point a = getPoint(0, 1);
		Point b = getPoint(u, 1 - vstep);
		Point c = getPoint(u + ustep, 1 - vstep);
		glVertex3d(a.x, a.y, a.z);
		glVertex3d(b.x, b.y, b.z);
		glVertex3d(c.x, c.y, c.z);
		glEnd();
	}
}	 






void init()
{
	glClearColor(0, 1, 1, 1);
	glClearDepth(1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0, 1, 1, 1);
	glClearDepth(1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	GLfloat light_position[] = { 1.0f, 1.0f, 1.0f, 0.0f };
	GLfloat light_ambient[] = { 0.2f, 0.2f, 0.2f, 0.2f };
	GLfloat light_diffuse[] = { 0.5f, 0.5f, 0.5f, 0.2f };
	GLfloat light_specular[] = { 0.5f, 0.5f, 0.5f, 0.2f };
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_AUTO_NORMAL);
	glEnable(GL_NORMALIZE);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);
}

void displayFunc()
{
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glColor3f(1.0, 0.0, 0.0);
	glPointSize(1.0);
	glRotated(30, 1, 0, 0);
	glRotated(60, 0, 1, 0);
	glRotated(90, 0, 0, 1);
	drawWire();
	glutSwapBuffers();
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(width, height);
	glutCreateWindow("Sphere");

	init();
	glutDisplayFunc(displayFunc);
	glutMainLoop();
}

画曲线

#include <iostream>
#include <Windows.h>

// GLEW    
#include <GL/glew.h>    

// GLFW 
#include <GLFW/glfw3.h>   

GLuint WIDTH = 400, HEIGHT = 400;
GLfloat gX = 0, gY = 0;
int choosedPoint = -1;
GLfloat pointWidth = 9.0f;

#define POINTS_CNT         6
#define VALUE_PER_POINT    3
static GLfloat controlPoints[POINTS_CNT][VALUE_PER_POINT] = {}, originPoints[POINTS_CNT][VALUE_PER_POINT] = {
	{-0.8f, 0.8f, 0.0f},
	{-0.8f, 0.0f, 0.0f},
	{0.0f, -0.0f, 0.0f},
	{0.0f, -0.0f, 0.0f},
	{0.8f, -0.0f, 0.0f},
	{0.8f, -0.8f, 0.0f}
};

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouseButton_callback(GLFWwindow* window, int button, int action, int mode);
void mouseMove_callback(GLFWwindow* window, double xPos, double yPos);
void windowResize_callback(GLFWwindow* window, int width, int height);

void resetControlPoints()
{
	for (int i = 0; i < POINTS_CNT; ++i)
	{
		for (int j = 0; j < VALUE_PER_POINT; ++j)
		{
			controlPoints[i][j] = originPoints[i][j];
		}
	}

}

//opengl在glfw中显示规则:
//1.画布中间坐标为0,0。长宽个1.0f个单位,超过1.0f的部分不会被绘制出来
void DrawLine()
{
	glColor4b(99, 38, 13, 90);

	glBegin(GL_LINES);

	glVertex2f(-1 + gX * 4, 1 + gY * 4);//指定第一个点
	glVertex2f(-0.8 + gX * 4, gY * 4 + 0.8);//指定第二个点

	glEnd();//结束
}

void DrawBezier()
{
	glColor3b(99, 38, 13);
	//第三个参数最好是1.0f,
	glMap1f(GL_MAP1_VERTEX_3, 0.0f, 0.8f, VALUE_PER_POINT, POINTS_CNT, &controlPoints[0][0]);
	glEnable(GL_MAP1_VERTEX_3);

	glBegin(GL_LINE_STRIP);

	//glEvalCoord1f,从u1取值到u2,线性均匀排列,取多少次就是分多少段,如果段数少,可能会有明显的曲折,最好是0-1.0f
	for (GLfloat i = 0; i <= 0.8; i += 0.02)
	{
		glColor3f(i, 1 - i, 0.4f + i);
		glEvalCoord1f(i);
	}

	glEnd();

	glColor3b(0, 0, 0);
	glPointSize(pointWidth);
	glBegin(GL_POINTS);
	for (int i = 0; i < POINTS_CNT; ++i)
	{
		glVertex3fv(controlPoints[i]);
	}
	glEnd();
}

void render(GLFWwindow* window)
{
	glClearColor(1.0f, 0.8f, 1.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);

	DrawLine();
	DrawBezier();
}

void initWindow(GLFWwindow* window)
{
	glfwMakeContextCurrent(window);

	//设置按键回调
	glfwSetKeyCallback(window, key_callback);
	//设置鼠标按键点下松开的回调
	glfwSetMouseButtonCallback(window, mouseButton_callback);
	//设置鼠标移动的回调
	glfwSetCursorPosCallback(window, mouseMove_callback);

	//设置窗口大小改变的回调,让绘画区域在窗口中间
	glfwSetWindowSizeCallback(window, windowResize_callback);
}

void initParam()
{
	//显示规则:窗口左下角坐标为0,0;所以下行代码表示在窗口左下角向右向上的400个像素单位作为画布
	glViewport(0, 0, WIDTH, HEIGHT);//设置显示区域400*400,但是可以拖动改变窗口大小
	glLineWidth(3.0);//设置线条宽度,3个像素

	glEnable(GL_BLEND);

	glEnable(GL_POINT_SMOOTH);
	glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);//设置点圆滑

	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);//设置线光滑

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

}

int main()
{
	glfwInit();

	GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	initWindow(window);



	initParam();
	resetControlPoints();

	// Game loop    
	while (!glfwWindowShouldClose(window))
	{
		glfwPollEvents();

		render(window);

		glfwSwapBuffers(window);
	}

	// Terminate GLFW, clearing any resources allocated by GLFW.    
	glfwTerminate();
	return 0;
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)//按下esc退出程序
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	else if (key == GLFW_KEY_LEFT)
	{
		gX -= 0.01;
	}
	else if (key == GLFW_KEY_RIGHT)
	{
		gX += 0.01;
	}
	else if (key == GLFW_KEY_UP)
	{
		gY += 0.01;
	}
	else if (key == GLFW_KEY_DOWN)
	{
		gY -= 0.01;
	}
}

//窗口坐标转视图坐标
void windowPos2CoordiatePos(GLFWwindow* window, double xPos, double yPos, double *pX, double *pY)
{
	int width, height;
	glfwGetWindowSize(window, &width, &height);

	xPos -= (width - WIDTH) / 2;
	*pX = xPos / (WIDTH / 2) - 1;

	yPos -= (height - HEIGHT) / 2;
	*pY = 1 - yPos / (HEIGHT / 2);
}

//x,y视图坐标系坐标转xpos、ypos窗口坐标
void coordiatePos2WindowPos(GLFWwindow* window, double x, double y, double *pXPos, double *pYPos)
{
	int width, height;
	glfwGetWindowSize(window, &width, &height);

	*pXPos = (x + 1) * (WIDTH / 2);
	*pYPos = (1 - y) * (WIDTH / 2);

	*pXPos += (width - WIDTH) / 2;
	*pYPos += (height - HEIGHT) / 2;
}

//判断一个窗口坐标是否在视图坐标系的某个点中,
bool posInPoint(GLFWwindow* window, double xPos, double yPos, double x, double y, double pointWidth)
{
	double trueXPos, trueYPos;
	coordiatePos2WindowPos(window, x, y, &trueXPos, &trueYPos);

	if (xPos >= trueXPos - pointWidth
		&& xPos <= trueXPos + pointWidth
		&& yPos >= trueYPos - pointWidth
		&& yPos <= trueYPos + pointWidth)
	{
		return true;
	}

	return false;
}

void mouseButton_callback(GLFWwindow* window, int button, int action, int mode)
{
	double xPos = 0, yPos = 0;
	double x = 0, y = 0;
	glfwGetCursorPos(window, &xPos, &yPos);

	WCHAR str[200];
	swprintf(str, 400, L"button %d, action %d, mode %x [%lf,%lf]\r\n", button, action, mode, xPos, yPos);
	//OutputDebugString(str);

	choosedPoint = -1;

	windowPos2CoordiatePos(window, xPos, yPos, &x, &y);

	for (int i = 0; i < POINTS_CNT && (GLFW_PRESS == action); ++i)
	{
		if (posInPoint(window, xPos, yPos, controlPoints[i][0], controlPoints[i][1], pointWidth))
		{
			choosedPoint = i;
			break;
		}
	}

	if (GLFW_PRESS == action && (GLFW_MOD_CONTROL & mode))
	{
		resetControlPoints();
	}
}

void mouseMove_callback(GLFWwindow* window, double xPos, double yPos)
{
	if (choosedPoint >= 0 && choosedPoint <= POINTS_CNT - 1)
	{
		double newX = 0, newY = 0;
		windowPos2CoordiatePos(window, xPos, yPos, &newX, &newY);
		controlPoints[choosedPoint][0] = newX;
		controlPoints[choosedPoint][1] = newY;
	}
}

void windowResize_callback(GLFWwindow* window, int width, int height)
{
	WIDTH = HEIGHT = width > height ? height : width;
	//左下角是0, 0坐标, x,y说就是相对于左下角的像素距离
	glViewport((width - WIDTH) / 2, (height - HEIGHT) / 2, WIDTH, HEIGHT);
}

不用系统函数画直线与圆

#include<stdio.h>
#include<vector>

#include<GL/glut.h>
using namespace std;
typedef long double db;
typedef pair<int, int> pii;
int WinWidth = 1024;
int WinHeight = 720;
int X0 = -1, X1, Y0, Y1, Xvalue;

struct node {
	int x0, y0, x1, y1;
};

vector<node>Vline, Vcircle;
vector<pii> Point;

void init();
void Mymenu(int);
void Drawlines(int, int, int, int);
void DrawCircle(int, int, int, int);

void drawvertex(int x, int y) {
	glBegin(GL_POINTS);
	glVertex2i(x, y);
	glEnd();
}
void ReDraw() {
	for (int i = 0; i < (int)Vline.size(); ++i)
		Drawlines(Vline[i].x0, Vline[i].y0, Vline[i].x1, Vline[i].y1);
	for (int i = 0; i < (int)Vcircle.size(); ++i)
		DrawCircle(Vcircle[i].x0, Vcircle[i].y0, Vcircle[i].x1, Vcircle[i].y1);

}
void myDisplay() {
	glClear(GL_COLOR_BUFFER_BIT);
	glRectf(-1.0, -1.0, 1.0, 1.0);
	ReDraw();
	glutSwapBuffers();
}



void update() {
	if (Xvalue == 1) {
		if (X0 == -1) return;
		if (Vline.size() == 0 || (Vline[Vline.size() - 1].x0 != X0 && Vline[Vline.size() - 1].y0 != Y0))
			Vline.push_back({ X0,Y0,X1,Y1 });
		else {
			Vline[Vline.size() - 1].x1 = X1;
			Vline[Vline.size() - 1].y1 = Y1;
		}
	}
	else if (Xvalue == 2) {
		if (X0 == -1) return;
		int len = Vcircle.size();
		if (Vcircle.size() == 0 || (Vcircle[len - 1].x0 != X0 && Vcircle[len - 1].y0 != Y0))
			Vcircle.push_back({ X0, Y0, X1, Y1 });
		else {
			Vcircle[len - 1].x1 = X1;
			Vcircle[len - 1].y1 = Y1;
		}
	}
	myDisplay();
}

void Drawlines(int x1, int y1, int x2, int y2) {
	int x = x1, y = y1, dx = abs(x2 - x1), dy = abs(y2 - y1), s1 = 1, s2 = 1, e, flag = 0;
	if (x1 >= x2) s1 = -1;
	if (y1 >= y2) s2 = -1;
	if (dy > dx) { swap(dx, dy); flag = 1; }
	e = -dx;
	int DX = 2 * dx, DY = 2 * dy;
	for (int i = 1; i <= dx; i++) {
		drawvertex(x, y);
		if (e >= 0) {
			if (!flag) y += s2;
			else x += s1;
			e = e - DX;
		}
		if (!flag) x += s1;
		else y += s2;
		e = e + DY;
	}
}

void Bresenham_Circle(int xc, int yc, int r) {
	int x, y, d;
	x = 0; y = r; d = 5 - 4 * r;
	drawvertex(x + xc, y + yc);
	while (x < y)
	{
		if (d < 0) d = d + 8 * x + 12;
		else { d = d + 8 * (x - y) + 20; y--; }
		x++;
		drawvertex(x + xc, y + yc); drawvertex(y + xc, x + yc);
		drawvertex(y + xc, -x + yc); drawvertex(x + xc, -y + yc);
		drawvertex(-x + xc, -y + yc); drawvertex(-y + xc, -x + yc);
		drawvertex(-x + xc, y + yc); drawvertex(-y + xc, x + yc);
	}
}

void DrawCircle(int x1, int y1, int x2, int y2) {
	int r = (int)sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
	Bresenham_Circle(x1, y1, r); //Bresenham算法

}
void Dragmouse(int x, int y) {
	// printf("%d %d\n",x,y);
	X1 = x; Y1 = y; update();
}

void Mymouse(int button, int state, int x, int y) {
	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
		// printf("%d UP %d\n",x,y );
		X1 = x; Y1 = y;
		update();
	}
	else if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
		// printf("%d down %d\n",x,y);
		X0 = x; Y0 = y;
	}
}

void Mymenu(int val) {
	Xvalue = val;
	if (val == 1) {
		X0 = -1;
	}
	else if (val == 2) {
		X0 = -1;
	}
	else if (val == 4) {
		exit(0);
	}
	else if (val == 3) {
		Vline.clear();
		Vcircle.clear();
		X0 = -1;
		update();
	}

}
int main(int argc, char *argv[]) {
	glutInit(&argc, argv); //初始化
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
	glutInitWindowPosition(50, 50);
	glutInitWindowSize(WinWidth, WinHeight);
	glutCreateWindow("test one");
	init();

	glutDisplayFunc(&myDisplay); //注册显示回调函数
	glutCreateMenu(Mymenu); //注册菜单回调函数
	glutAddMenuEntry("DrawLine", 1);
	glutAddMenuEntry("DrawCircle", 2);
	glutAddMenuEntry("ClearScreen", 3);
	glutAddMenuEntry("Exit", 4);
	glutAttachMenu(GLUT_RIGHT_BUTTON); //把当前菜单注册到指定的鼠标键
	glutMainLoop();
	return 0;
}

void init() {
	glClearColor(0, 0, 0, 1); //(red green blue alpha) alpha表示混合因子
	glClear(GL_COLOR_BUFFER_BIT); //将屏幕所有像素点还原为"底色"
	glPointSize(3.0f); //指定栅格化点的直径
	glMatrixMode(GL_PROJECTION); //声明接下来要进行的操作,GL_PROJECTION 投影, GL_MODELVIEW 模型视图, GL_TEXTURE 纹理
	glLoadIdentity(); //加载一个单位矩阵
	gluOrtho2D(0, WinWidth, WinHeight, 0); //定义裁剪面
	glColor3f(255, 215, 0); //设置画点颜色 R-G-B
	glutMouseFunc(Mymouse);
	glutMotionFunc(Dragmouse);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值