osg - 光照

        OSG全面支持 OpenGL 的光照特性,包括材质属性(material property)、光照属性(light property)和光照模型 (lighting model)。与 OpenGL 相似,OSG中的光源也是不可见的,而非渲染一个灯泡或其他自然形状。同样,光源会创建着色效果,但并不创建阴影,osgShadow 可以用来创建阴影。

1.osg::Light类

        OSG将OpenGL中的 glLight()作了一个 light状态的类封装,用于保存灯光的模式与属性参数信息。osg::Light 类派生自osg::StateAttribute类,继承了对模式与属性参数信息的操作接口。在osg::light类中通过 apply(State&state)函数将灯光的状态参数信息应用到OpenGL 的状态机中。osg::Light的继承关系图如图5-23 所示。

图5-23 osg::Light的继承关系图

        osg::Light类包括的属性参数如下:

  1. int _lightnum;  // 灯光数量  
  2. Vec4 _ambient;  // 环境光颜色  
  3. Vec4 _diffuse;  // 漫光颜色  
  4. Vec4 _specular; // 镜面光颜色  
  5. Vec3 _position; // 光源的位置信息  
  6. Vec3 _direction;// 光源的方向  
  7. float _constant_attenuation; // 常最衰减  
  8. float _linear_attenuation;   // 线性衰减  
  9. float _quadratic_attenuation;// 二次方衰减  
  10. float _spot_exponent; // 指数衰减  
  11. float_spot_cutoff;    // 关闭衰减(spread)  

        上面的参数应该都比较容易理解。OSG 支持最多8个光源,即GL_LIGHTO~GL_LIGHT7,这与读者的OpenGL版本也有关系。

2.osg::LightSource类

        osg::LightSource 类直接继承自 osg::Group。作为一个灯光管理类,继承了 osg::Group 类的管理节点的接口;将灯光作为一个节点可以加入到场景图中进行渲染。继承关系图如图 5-24 所示。

图5-24 osg::LightSource 的继承关系图

            osg::LightSource类中的成员函数为:

  1. void setReferenceFrame(ReferenceFrame rf);// 设置赖引用  

        帧引用包括如下两个枚举变量:

  1. enum ReferenceFrame  
  2. {  
  3.     RELATIVE_RF, // 相对顿引用  
  4.     ABSOLUTE_RF  // 绝对引用  
  5. }  

        设置光源的引用帧时,不是相对于父节点的帧引用,就是相对于绝对坐标的帧,默认的设置为RELATIVE_RF,设置引用为RELATIVE_RF 同样会设置CullingActive 的标志量为开(ON)状态,并且对它的父节点也起作用;否则,对它与它所有的父节点都禁用拣选(Culling),对防止不合适的拣选是必需的,如果绝对光源在场景图的深处将会对拣选的时间有影响,因此,推荐在场景的顶部使用绝对的光源。

 3.场景中使用光源

        在一个场景中添加光源主要包括以下步骤

        (1)指定场景模型的法线。

        (2)允许光照并设置光照状态。

        (3指定光源属性并关联到场景图形

        对于场景中的模型,只有当其中设有单位法线时才会正确地显示光照。当场景中的模型没有指定法线时,可以用前面讲到的osgUtl::SmoothingVisitor自动生成法线。需要注意的是,法向量必须单位化。有时场景中的模型虽然指定了单位法向量,但是光照的计算结果过于明亮或过于暗淡(可能是缩放变换造成的),这时最有效的解决方案是在 StateSet 中允许法线的重放缩模式,代码如下:

  1. osg::StateSet *state = geode->setOrCreateStateSet():  
  2. state->setMode(GL_RESCALE_NORMAL,osg::StateAttribute::ON);  

        与在OpenGL中相同,这一特性可以保证法线在均匀放缩变换时仍然保持单位长度。如果场景中的放缩变换是非均匀的,那么读者可以允许法线归一化模式,以保证法线为单位长度。由于要进行法线的重新放缩,归一化模式往往会耗费大量的时间,编程时要尽量避免。归一化模式的代码如下:

  1. osg::StateSet *state = geode->setOrCreateStateSet():  
  2. state->setMode(GL_NORMALIZE,osg::StateAttribute::ON);  

        要在OSG中获得光照效果,需要允许光照并至少允许一个光源。程序osgviewer 在默认情况下就是这样做的,它在根节点的 StateSet 中已经设置了相应的模式。读者可以在自己的程序中进行相同的设置。下面的代码段用于允许光照并为根节点的 StateSet 允许两个光源(GL_LIGHTO和GL_LIGHT1)

  1. osg::StateSet *state = geode->setOrCreateStateSet():  
  2. state->setMode(GL_LIGHTING,osg::StateAttribute::ON);  
  3. state->setMode(GL_LIGHT0,osg::StateAttribute::ON);  
  4. state->setMode(GL_LIGHT1,osg::StateAttribute::ON);  

        在场景中添加一个光源,可以创建一个osg::Light对象以定义光源参数,然后将osg::Light添加到一个osg::LightSource节点中,并将 LightSource 节点添加到场景图形。osg::LightSource 是一个包含了唯一的Light定义的高效的组节点,而由osg::Light 定义的光源将对整个场景产生影响。下面的代码实现将osg::Light添加到osg::LightSource对象中;

  1. osg::ref_ptr<osg::LightSource> ls = new osg::LightSource;  
  2. ls->setLight(light.get()); 

        在实际生活中,当光照照射到物体上时都会反射等现象,所以,在对光源的设置完成以后需要设置模型的表面材质,第5.4节会讲到,下面先看看关于光照的两个示例。

4.简单光源示例

        简单光源 (osg::LightSource)示例的代码如程序清单5-12 所示:

1.	osg::ref_ptr<osg::Group> createLight(osg::ref_ptr<osg::Node> node) //向场景中添加光源  
2.	{  
3.	    osg::ref_ptr<osg::Group> lightRoot = new osg::Group();  
4.	    lightRoot->addChild(node);  
5.	  
6.	    // 开启光照  
7.	    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();  
8.	    stateset = lightRoot->getOrCreateStateSet();  
9.	    stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);  
10.	    stateset->setMode(GL_LIGHT0, osg::StateAttribute::ON);  
11.	  
12.	    // 计算包围盒  
13.	    osg::BoundingSphere bs;  
14.	    node->computeBound();  
15.	    bs = node->getBound();  
16.	  
17.	    // 创建一个Light对象  
18.	    osg::ref_ptr<osg::Light> light = new osg::Light();  
19.	    light->setLightNum(0);     
20.	    light->setDirection(osg::Vec3(0.0f, 0.0f, -1.0f));// 设置方向  
21.	    light->setPosition(osg::Vec4(bs.center().x(), bs.center().y(), bs.center().z() + bs.radius(), 1.0f));// 设置位置   
22.	    light->setAmbient(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));// 设置环境光的颜色   
23.	    light->setDiffuse(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); // 设置散射光的颜色      
24.	    light->setConstantAttenuation(1.0f);// 设置恒衰减指数     
25.	    light->setLinearAttenuation(0.0f);  // 设置线形衰减指数    
26.	    light->setQuadraticAttenuation(0.0f);// 设置二次方衰减指数  
27.	  
28.	    // 创建光源  
29.	    osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource();  
30.	    lightSource->setLight(light.get());  
31.	    lightRoot->addChild(lightSource.get());  
32.	  
33.	    return lightRoot.get();  
34.	}  
35.	  
36.	void lightSource_5_12(const string  &strDataFolder)  
37.	{  
38.	    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();  
39.	    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;  
40.	    traits->x = 40;  
41.	    traits->y = 40;  
42.	    traits->width = 600;  
43.	    traits->height = 480;  
44.	    traits->windowDecoration = true;  
45.	    traits->doubleBuffer = true;  
46.	    traits->sharedContext = 0;  
47.	  
48.	    osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());  
49.	    osg::ref_ptr<osg::Camera> camera = new osg::Camera;  
50.	    camera->setGraphicsContext(gc.get());  
51.	    camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));  
52.	    GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;  
53.	    camera->setDrawBuffer(buffer);  
54.	    camera->setReadBuffer(buffer);  
55.	    viewer->addSlave(camera.get());  
56.	  
57.	    osg::ref_ptr<osg::Group> root = new osg::Group();  
58.	  
59.	    // 读取模型  
60.	    string strDataPath = strDataFolder + "cow.osg";  
61.	    osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);  
62.	  
63.	    // 向场景中添加光源  
64.	    root->addChild(createLight(node.get()));  
65.	  
66.	    // 优化场景数据  
67.	    osgUtil::Optimizer optimizer;  
68.	    optimizer.optimize(root.get());  
69.	  
70.	    viewer->setSceneData(root.get());  
71.	    viewer->realize();  
72.	    viewer->run();  
73.	}  

        运行程序,截图如图5-25 所示

图5-25 简单光源示例裁图

4.聚光灯示例

        聚光灯(SpotLight)示例的代码如程序清单5-13所示:

 

1.	osg::ref_ptr<osg::Image> createSpotLightImage(const osg::Vec4& centerColour,   
2.	    const osg::Vec4& backgroudColour, unsigned int size, float power) // 创建聚光灯纹理的mipmap贴图  
3.	{  
4.	    // 创建Image对象  
5.	    osg::ref_ptr<osg::Image> image = new osg::Image;    
6.	    image->allocateImage(size, size, 1, GL_RGBA, GL_UNSIGNED_BYTE); // 动态分配一个size*size大小的image  
7.	  
8.	    // 填充image  
9.	    // 以中心为原点,颜色逐渐向四周衰减  
10.	    float mid = (float(size) - 1)*0.5f;  
11.	    float div = 2.0f / float(size);  
12.	    for (unsigned int r = 0; r < size; ++r)  
13.	    {  
14.	        unsigned char* ptr = image->data(0, r, 0);  
15.	        for (unsigned int c = 0; c < size; ++c)  
16.	        {  
17.	            float dx = (float(c) - mid)*div;  
18.	            float dy = (float(r) - mid)*div;  
19.	            float r = powf(1.0f - sqrtf(dx*dx + dy*dy), power);  
20.	            if (r < 0.0f) r = 0.0f;  
21.	            osg::Vec4 color = centerColour*r + backgroudColour*(1.0f - r);  
22.	            *ptr++ = (unsigned char)((color[0])*255.0f);  
23.	            *ptr++ = (unsigned char)((color[1])*255.0f);  
24.	            *ptr++ = (unsigned char)((color[2])*255.0f);  
25.	            *ptr++ = (unsigned char)((color[3])*255.0f);  
26.	        }  
27.	    }  
28.	    return image.get();  
29.	}  
30.	  
31.	osg::ref_ptr<osg::StateSet> createSpotLightDecoratorState(unsigned int lightNum,   
32.	    unsigned int textureUnit) // 创建聚光灯状态属性  
33.	{  
34.	    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;  
35.	  
36.	    // 开启ID为lightNum的光照  
37.	    stateset->setMode(GL_LIGHT0 + lightNum, osg::StateAttribute::ON);  
38.	  
39.	    // 设置中心的颜色和环境光的颜色  
40.	    osg::Vec4 centerColour(1.0f, 1.0f, 1.0f, 1.0f);  
41.	    osg::Vec4 ambientColour(0.05f, 0.05f, 0.05f, 1.0f);  
42.	  
43.	    // 创建聚光灯纹理  
44.	    osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();  
45.	    texture->setImage(createSpotLightImage(centerColour, ambientColour, 64, 1.0));  
46.	    texture->setBorderColor(osg::Vec4(ambientColour));  
47.	    texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER);  
48.	    texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER);  
49.	    texture->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_BORDER);  
50.	  
51.	    // 打开纹理单元  
52.	    stateset->setTextureAttributeAndModes(textureUnit, texture.get(), osg::StateAttribute::ON);  
53.	  
54.	    // 设置自动生成纹理坐标  
55.	    stateset->setTextureMode(textureUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON);  
56.	    stateset->setTextureMode(textureUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON);  
57.	    stateset->setTextureMode(textureUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON);  
58.	    stateset->setTextureMode(textureUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON);  
59.	  
60.	    return stateset.get();  
61.	}  
62.	  
63.	osg::ref_ptr<osg::Node> createSpotLightNode(const osg::Vec3& position,   
64.	    const osg::Vec3& direction, float angle, unsigned int lightNum,   
65.	    unsigned int textureUnit) // 创建聚光灯节点  
66.	{  
67.	    osg::ref_ptr<osg::Group> group = new osg::Group;  
68.	  
69.	    // 创建光源  
70.	    osg::ref_ptr<osg::LightSource> lightsource = new osg::LightSource;  
71.	    osg::ref_ptr<osg::Light> light = lightsource->getLight();  
72.	    light->setLightNum(lightNum);  
73.	    light->setPosition(osg::Vec4(position, 1.0f));  
74.	    light->setAmbient(osg::Vec4(0.00f, 0.00f, 0.05f, 1.0f));  
75.	    light->setDiffuse(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));  
76.	    group->addChild(lightsource);  
77.	  
78.	    // 计算法向量  
79.	    osg::Vec3 up(0.0f, 0.0f, 1.0f);  
80.	    up = (direction ^ up) ^ direction;  
81.	    up.normalize();  
82.	  
83.	    // 创建自动生成纹理坐标节点  
84.	    osg::ref_ptr<osg::TexGenNode> texgenNode = new osg::TexGenNode;     
85.	    texgenNode->setTextureUnit(textureUnit); // 关联纹理单元  
86.	      
87.	    osg::ref_ptr<osg::TexGen> texgen = texgenNode->getTexGen(); // 设置纹理坐标生成器     
88.	    texgen->setMode(osg::TexGen::EYE_LINEAR); // 设置模式为视觉线性     
89.	    texgen->setPlanesFromMatrix(osg::Matrixd::lookAt(position, position + direction, up)*  
90.	        osg::Matrixd::perspective(angle, 1.0, 0.1, 100)); // 从视图中指定参考平面  
91.	    group->addChild(texgenNode.get());  
92.	  
93.	    return group.get();  
94.	}  
95.	  
96.	osg::ref_ptr<osg::AnimationPath> createAnimationPath(const osg::Vec3& center,   
97.	    float radius, double looptime) // 创建动画路径(请参看后面章节的OSG动画)  
98.	{  
99.	    osg::ref_ptr<osg::AnimationPath> animationPath = new osg::AnimationPath;  
100.	    animationPath->setLoopMode(osg::AnimationPath::LOOP);  
101.	  
102.	    int numSamples = 40;  
103.	    float yaw = 0.0f;  
104.	    float yaw_delta = 2.0f*osg::PI / ((float)numSamples - 1.0f);  
105.	    float roll = osg::inDegrees(30.0f);  
106.	  
107.	    double time = 0.0f;  
108.	    double time_delta = looptime / (double)numSamples;  
109.	    for (int i = 0; i < numSamples; ++i)  
110.	    {  
111.	        osg::Vec3 position(center + osg::Vec3(sinf(yaw)*radius, cosf(yaw)*radius, 0.0f));  
112.	        osg::Quat rotation(osg::Quat(roll, osg::Vec3(0.0, 1.0, 0.0))*osg::Quat(-(yaw + osg::inDegrees(90.0f)), osg::Vec3(0.0, 0.0, 1.0)));  
113.	  
114.	        animationPath->insert(time, osg::AnimationPath::ControlPoint(position, rotation));  
115.	  
116.	        yaw += yaw_delta;  
117.	        time += time_delta;  
118.	  
119.	    }  
120.	    return animationPath.get();  
121.	}  
122.	  
123.	osg::ref_ptr<osg::Node> createBase(const string &strDataFolder, const osg::Vec3& center, float radius)// 创建地形平面  
124.	{  
125.	    osg::ref_ptr<osg::Geode> geode = new osg::Geode;  
126.	  
127.	    osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();  
128.	    string strDataPath = strDataFolder + "Images/lz.rgb";  
129.	    osg::ref_ptr<osg::Image> image = osgDB::readImageFile(strDataPath);  
130.	    if (image.get())  
131.	    {  
132.	        osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;  
133.	        texture->setImage(image.get());  
134.	        stateset->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);  
135.	    }  
136.	  
137.	    geode->setStateSet(stateset.get());  
138.	  
139.	    osg::ref_ptr<osg::HeightField> grid = new osg::HeightField;  
140.	    grid->allocate(38, 39);  
141.	    grid->setOrigin(center + osg::Vec3(-radius, -radius, 0.0f));  
142.	    grid->setXInterval(radius*2.0f / (float)(38 - 1));  
143.	    grid->setYInterval(radius*2.0f / (float)(39 - 1));  
144.	  
145.	    float minHeight = FLT_MAX;  
146.	    float maxHeight = -FLT_MAX;  
147.	  
148.	  
149.	    unsigned int r;  
150.	    for (r = 0; r < 39; ++r)  
151.	    {  
152.	        for (unsigned int c = 0; c < 38; ++c)  
153.	        {  
154.	            float h = vertex[r + c * 39][2];  
155.	            if (h > maxHeight) maxHeight = h;  
156.	            if (h < minHeight) minHeight = h;  
157.	        }  
158.	    }  
159.	  
160.	    float hieghtScale = radius*0.5f / (maxHeight - minHeight);  
161.	    float hieghtOffset = -(minHeight + maxHeight)*0.5f;  
162.	  
163.	    for (r = 0; r < 39; ++r)  
164.	    {  
165.	        for (unsigned int c = 0; c < 38; ++c)  
166.	        {  
167.	            float h = vertex[r + c * 39][2];  
168.	            grid->setHeight(c, r, (h + hieghtOffset)*hieghtScale);  
169.	        }  
170.	    }  
171.	  
172.	    geode->addDrawable(new osg::ShapeDrawable(grid.get()));  
173.	  
174.	    osg::ref_ptr<osg::Group> group = new osg::Group;  
175.	    group->addChild(geode.get());  
176.	  
177.	    return group.get();  
178.	  
179.	}  
180.	  
181.	osg::ref_ptr<osg::Node> createMovingModel(const string &strDataFolder, const osg::Vec3& center,   
182.	    float radius) // 创建动画模型  
183.	{  
184.	    float animationLength = 10.0f;  
185.	    osg::ref_ptr<osg::AnimationPath> animationPath = createAnimationPath(center, radius, animationLength);  
186.	  
187.	    osg::ref_ptr<osg::Group> model = new osg::Group;  
188.	    string strDataPath = strDataFolder + "cessna.osg";  
189.	    osg::ref_ptr<osg::Node> cessna = osgDB::readNodeFile(strDataPath);  
190.	    if (cessna.get())  
191.	    {  
192.	        const osg::BoundingSphere& bs = cessna->getBound();  
193.	  
194.	        float size = radius / bs.radius()*0.3f;  
195.	        osg::ref_ptr<osg::MatrixTransform> positioned = new osg::MatrixTransform;  
196.	        positioned->setDataVariance(osg::Object::STATIC);  
197.	        positioned->setMatrix(osg::Matrix::translate(-bs.center())*  
198.	            osg::Matrix::scale(size, size, size)*  
199.	            osg::Matrix::rotate(osg::inDegrees(180.0f), 0.0f, 0.0f, 2.0f));  
200.	  
201.	        positioned->addChild(cessna.get());  
202.	  
203.	        osg::ref_ptr<osg::MatrixTransform> xform = new osg::MatrixTransform;  
204.	        xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath, 0.0f, 2.0));  
205.	        xform->addChild(positioned);  
206.	  
207.	        // 添加聚光灯节点  
208.	        xform->addChild(createSpotLightNode(osg::Vec3(0.0f, 0.0f, 0.0f), osg::Vec3(0.0f, 1.0f, -1.0f), 60.0f, 0, 1));  
209.	  
210.	        model->addChild(xform.get());  
211.	    }  
212.	  
213.	    return model.get();  
214.	}  
215.	  
216.	osg::ref_ptr<osg::Node> createModel(const string &strDatafolder) // 创建场景  
217.	{  
218.	    osg::Vec3 center(0.0f, 0.0f, 0.0f);  
219.	    float radius = 100.0f;  
220.	  
221.	    // 创建动画模型  
222.	    osg::ref_ptr<osg::Node> shadower = createMovingModel(strDatafolder, center, radius*0.5f);  
223.	  
224.	    // 创建地形平面  
225.	    osg::ref_ptr<osg::Node> shadowed = createBase(strDatafolder, center - osg::Vec3(0.0f, 0.0f, radius*0.1), radius);  
226.	  
227.	    // 创建场景组节点  
228.	    osg::ref_ptr<osg::Group> root = new osg::Group;     
229.	    root->setStateSet(createSpotLightDecoratorState(0, 1));// 设置状态属性   
230.	    root->addChild(shadower.get()); // 添加子节点  
231.	    root->addChild(shadowed.get());  
232.	  
233.	    return root.get();  
234.	}  
235.	  
236.	void spotLight_5_13(const string &strDatafolder)  
237.	{  
238.	    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();  
239.	    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;  
240.	    traits->x = 40;  
241.	    traits->y = 40;  
242.	    traits->width = 600;  
243.	    traits->height = 480;  
244.	    traits->windowDecoration = true;  
245.	    traits->doubleBuffer = true;  
246.	    traits->sharedContext = 0;  
247.	  
248.	    osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());  
249.	    osg::ref_ptr<osg::Camera> camera = new osg::Camera;  
250.	    camera->setGraphicsContext(gc.get());  
251.	    camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));  
252.	    GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;  
253.	    camera->setDrawBuffer(buffer);  
254.	    camera->setReadBuffer(buffer);  
255.	    viewer->addSlave(camera.get());  
256.	  
257.	    osg::ref_ptr<osg::Group> root = new osg::Group();  
258.	  
259.	    // 添加场景  
260.	    root->addChild(createModel(strDatafolder));  
261.	  
262.	    // 优化场景数据  
263.	    osgUtil::Optimizer optimizer;  
264.	    optimizer.optimize(root.get());  
265.	  
266.	    viewer->setSceneData(root.get());  
267.	    viewer->realize();  
268.	    viewer->run();  
269.	}  

        运行程序,截图如图5-26所示

图5-26 聚光示例截图

  • 25
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听风者868

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值