openGL之光照2(材料、颜色追踪、光源)---openGL学习笔记(十)

材料:

我们知道,决定物体实际颜色的是散射光,确切的说是材料的散射光反射率,而且openGL中,我们一般把材料的环境光和反射光反射率设置成同样的值,那么在设置过了全局环境光之后,就要设置材料的反射率:

//设置材料的反射率(环境光和散射光)
gl.glLightfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT_AND_DIFFUSE,BufferUtils.arr2FloatBuffer(ambient_and_diffuse));

注:第一个参数为正面和背面的反射率,第二个参数为环境光和散射光反射率,第三个参数同样是,把四个浮点数的分量值组成的float数组转换成缓冲区数据。

这样在启用光照的时候就可以观察到在环境光一定(默认值为0.2)的情况下,材料不同反射率的不同效果。

颜色追踪:

openGL中提供里两种方法设置材料的反射率,如上是一种,还有一种是启用颜色追踪:

颜色追踪顾名思义就是利用组成颜色的四个分量值来表示光的成分,颜色追踪同样是状态机,所以开启颜色追踪:

//启用颜色追踪
gl.glEnable(GL10.GL_COLOR_MATERIAL);
此时openGL就会从颜色状态机(  gl.glColor4f()  )中拿到各个分量值,从而设置光的成分。

光源:

openGL至少支持8中光源:光源0~7。

在我们启用了光照、设置了材料的反射率之后,我们所绘制的物体还是不太容易看清楚,因为此时物体的光照主要来自于全局环境光,而这部分光是很少量的,所以,我们要看的清楚所绘制的物体,就至少要启动一个光源:

//启用光源0
gl.glEnable(GL10.GL_LIGHT0);
那么同样的,启用一个光源之后就要为这个光源设置环境光、散射光、以及镜面光(如需)。

//设置光源0的环境光
float [] light0Ambient = { r3,g3,b3,a3 };
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT,BufferUtils.arr2FloatBuffer(light0Ambient));
//设置光源0的散射光
float [] light0Diffuse = { r4,g4,b4,a4 };
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE,BufferUtils.arr2FloatBuffer(light0Diffuse));
设置好了光源0光的分量后,还需指定光源0的位置:

//光源0的位置(1.0f表示光源就在此处,0表示位于无限远处---平行光)
float [] light0Position = {x,y,z,1.0f};
gl.glLightfv( GL10.GL_LIGHT0,GL10.GL_POSITION,BufferUtils.arr2FloatBuffer(light0Position) );
注:x,y,z是坐标轴,第四个参数只能有两个值1或者0,为1时代表光源就在此处,为0时代表光源位于无限远处,所以照射过来的光就是平行光。

效果图:


附代码:

public class MyLightingRenderer extends AbstractRenderer{
    @Override
    public void onDrawFrame(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);//设置清屏色
        gl.glColor4f(1f, 1f, 1f, 1f);//设置绘图颜色
        gl.glMatrixMode(GL10.GL_MODELVIEW);//模型视图矩阵
        gl.glLoadIdentity();//加载单位矩阵
        GLU.gluLookAt(gl, 0, 0, 5, 0, 0, 0, 0, 1, 0);//放置眼球位置
        gl.glRotatef(xRotate, 1, 0, 0);//x轴旋转角度
        gl.glRotatef(yRotate, 0, 1, 0);//y轴旋转角度
        gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);
        gl.glEnable(GL10.GL_DEPTH_TEST);
        gl.glColor4f(color_r, color_g, color_b, color_a);
        gl.glShadeModel(GL10.GL_FLAT);

        /*********************光照**************************/
        if(open_lighting){
            //启用光照
            gl.glEnable(GL10.GL_LIGHTING);
        }else{
            //禁止光照
            gl.glDisable(GL10.GL_LIGHTING);
        }
        float [] global_ambient = {r,g,b,a,};
        float [] ambient_and_diffuse = {r1,g1,b1,a1};
        //设置全局环境光
        gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, BufferUtils.arr2FloatBuffer(global_ambient));
        //设置材料的反射率(环境光和散射光)
        gl.glLightfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT_AND_DIFFUSE,BufferUtils.arr2FloatBuffer(ambient_and_diffuse));
        //颜色追踪
        if(open_ColorMatrial){
            //启用颜色追踪
            gl.glEnable(GL10.GL_COLOR_MATERIAL);
        } else {
            //禁止颜色追踪
            gl.glDisable(GL10.GL_COLOR_MATERIAL);
        }
        if(open_light0){
            //启用光源0
            gl.glEnable(GL10.GL_LIGHT0);
        }else {
            //禁止光源0
            gl.glDisable(GL10.GL_LIGHT0);
        }
        //设置光源0的环境光
        float [] light0Ambient = { r3,g3,b3,a3 };
        gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT,BufferUtils.arr2FloatBuffer(light0Ambient));
        //设置光源0的散射光
        float [] light0Diffuse = { r4,g4,b4,a4 };
        gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE,BufferUtils.arr2FloatBuffer(light0Diffuse));
        //光源0的位置(1.0f表示光源就在此处,0表示位于无限远处---平行光)
        float [] light0Position = {x,y,z,1.0f};
        gl.glLightfv( GL10.GL_LIGHT0,GL10.GL_POSITION,BufferUtils.arr2FloatBuffer(light0Position) );
        //画一个球
        BufferUtils.drawSphere(gl,0.4f,8,8);
    }
}
MainActivity:

public class MainActivity extends Activity {
    private AbstractRenderer renderer;
    MyGLSurfaceView view;
    private Dialog d;
    private Resources r;


    protected void onCreate(Bundle savedInstanceState) {
        r = this.getResources();
        super.onCreate(savedInstanceState);
        view = new MyGLSurfaceView(this);
        //render渲染器
        renderer = new MyLightingRenderer();
//        view.setEGLConfigChooser(5,6,5,0,8,8);
        view.setRenderer(renderer);
//        view.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//默认渲染模式,持续渲染。
//        view.setRenderMode( GLSurfaceView.RENDERMODE_WHEN_DIRTY );//脏渲染(命令渲染)脏渲染模式下ondrawframe()方法只调用一次。
        setContentView(view);
        buildDialog();
        addCheckBoxEvent();
    }

    //添加复选框事件
    private void addCheckBoxEvent() {
        //匿名内部类:box监听
        CompoundButton.OnCheckedChangeListener box1 = new CompoundButton.OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                /*
                //通过反射实现
                try {
                    int id = buttonView.getId();
                    String name = r.getResourceName( id );
                    Log.i("xiaoyu",name);
                    name.substring(name.lastIndexOf("_") + 1);
                    Class clazz = renderer.getClass();
                    Field f = clazz.getField(name);
                    f.setAccessible(true);
                    f.set(renderer,isChecked);
                } catch (Exception e) {
                    e.printStackTrace();
                }*/
                switch (buttonView.getId()){
                    case R.id.cb_open_lighting:
                        renderer.open_lighting = isChecked;
                    case R.id.cb_open_ColorMatrial:
                        renderer.open_ColorMatrial = isChecked;
                    case R.id.cb_open_light0:
                        renderer.open_light0 = isChecked;
                }


            }
        };
        //seekbar监听
        final SeekBar.OnSeekBarChangeListener bar1 = new SeekBar.OnSeekBarChangeListener(){
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                float scale = (float)progress/(float)seekBar.getMax();
                switch (seekBar.getId()){
                    //全局环境光的三个seekbar
                    case R.id.sb_r:
                        renderer.r = scale;
                    case R.id.sb_g:
                        renderer.g = scale;
                    case R.id.sb_b:
                        renderer.b = scale;
                    //材料的环境光和散射光的反射率的三个seekbar
                    case R.id.sb_r1:
                        renderer.r1 = scale;
                    case R.id.sb_g1:
                        renderer.g1 = scale;
                    case R.id.sb_b1:
                        renderer.b1 = scale;
                    //颜色追踪器的三个seekbar
                    case R.id.sb_r2:
                        renderer.color_r = scale;
                    case R.id.sb_g2:
                        renderer.color_g = scale;
                    case R.id.sb_b2:
                        renderer.color_b = scale;
                    //光源0环境光的三个seekbar
                    case R.id.sb_r3_ambient:
                        renderer.r3 = scale;
                    case R.id.sb_g3_ambient:
                        renderer.g3 = scale;
                    case R.id.sb_b3_ambient:
                        renderer.b3 = scale;
                    //光源0散射光的三个seekbar
                    case R.id.sb_r3_diffuse:
                        renderer.r4 = scale;
                    case R.id.sb_g3_diffuse:
                        renderer.g4 = scale;
                    case R.id.sb_b3_diffuse:
                        renderer.b4 = scale;
                    //光源0位置
                    case R.id.sb_x:
                        renderer.x = scale * 20 - 10f;
                    case R.id.sb_y:
                        renderer.y = scale * 20 - 10f;
                    case R.id.sb_z:
                        renderer.z = scale * 20 - 10f;
                }
            }
            public void onStartTrackingTouch(SeekBar seekBar) {

            }
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        };
/**************************利用反射动态给CheckBoxSeekBar加监听器*********************************/
        try {
            Class clazz = R.id.class;
            Field [] fs = clazz.getDeclaredFields();//得到R的所有字段

            CheckBox box = null;
            SeekBar bar = null;
            //遍历fs,因为R文件中的每个字段都是静态整形值,所以,直接f.get(null)
            for( Field f : fs ){
                int id = (Integer)f.get(null);
                View view = d.findViewById(id);
                //判断控件类型,添加不同监听器。
                if( view != null ){
                    if( view instanceof CheckBox ){
                        box = (CheckBox) view;
                        box.setOnCheckedChangeListener(box1);
                    }else if( view instanceof SeekBar ){
                        bar = (SeekBar) view;
                        bar.setOnSeekBarChangeListener(bar1);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void buildDialog() {
        d = new Dialog(this,android.R.style.Theme_Translucent_NoTitleBar_Fullscreen);
        d.setContentView(R.layout.light_setting);
        d.show();
    }


    class MyGLSurfaceView extends GLSurfaceView{

        public MyGLSurfaceView(Context context) {
            super(context);
        }

        public MyGLSurfaceView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        }


    //触摸时纵向旋转
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float step = 1f;
        renderer.xRotate = renderer.xRotate - step;


        return super.onTouchEvent(event);
    }

    //按返回键横向旋转

    @Override
    public void onBackPressed() {
        float step = 1f;
        renderer.yRotate = renderer.yRotate - step;
    }



}
缓冲区工具:

public class BufferUtils {

    /**
     * 将浮点数组转换成字节缓冲区
     *
     * @param arr
     * @return ibb
     */

    public static ByteBuffer array2ByteBuffer(float[] arr) {

        ByteBuffer ibb = ByteBuffer.allocateDirect(arr.length * 4);//一个浮点数等于四个字节
        ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序
        FloatBuffer fbb = ibb.asFloatBuffer();//将字节缓冲转换成浮点缓冲
        fbb.put(arr);
        ibb.position(0);//把缓冲区指针位置定位到0位置

        return ibb;
    }

    /**
     * list集合转换成字节缓冲区
     *
     * @param list
     * @return ibb
     */
    public static ByteBuffer list2ByteBuffer(List<Float> list) {

        ByteBuffer ibb = ByteBuffer.allocateDirect(list.size() * 4);//一个浮点数等于四个字节
        ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序
        FloatBuffer fbb = ibb.asFloatBuffer();//将字节缓冲转换成浮点缓冲
        //用增强for循环把每个坐标put进去
        for (Float f : list) {
            fbb.put(f);
        }
        ibb.position(0);//把缓冲区指针位置定位到0位置

        return ibb;
    }

    /**
     * list集合转换成Float缓冲区
     *
     * @param list
     */

    public static FloatBuffer list2FloatBuffer(List<Float> list) {

        ByteBuffer ibb = ByteBuffer.allocateDirect(list.size() * 4);//一个浮点数等于四个字节
        ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序
        FloatBuffer fbb = ibb.asFloatBuffer();//将字节缓冲转换成浮点缓冲
        //用增强for循环把每个坐标put进去
        for (Float f : list) {
            fbb.put(f);
        }
        fbb.position(0);//把缓冲区指针位置定位到0位置
        return fbb;
    }

    /**
     * 将浮点数组转换为浮点缓冲区
     *
     * @param coords
     * @return fbb
     */

    public static FloatBuffer arr2FloatBuffer(float[] coords) {
        ByteBuffer ibb = ByteBuffer.allocateDirect(coords.length * 4);//一个浮点数等于四个字节
        ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序
        FloatBuffer fbb = ibb.asFloatBuffer();//将字节缓冲转换成浮点缓冲
        fbb.put(coords);
        fbb.position(0);//把缓冲区指针位置定位到0位置
        return fbb;

    }

    /**
     * 将字节数组转换为浮点缓冲区
     *
     * @param arr
     * @return ibb
     */

    public static ByteBuffer arr2ByteBuffer(byte[] arr) {
        ByteBuffer ibb = ByteBuffer.allocateDirect(arr.length);
        ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序
        ibb.put(arr);
        ibb.position(0);
        return ibb;

    }

    /**
     * 绘制球体
     *
     * @param gl
     * @param r
     * @param stack
     * @param slice
     */

    public static void drawSphere(GL10 gl, float r, int stack, int slice) {

        float statckStep = (float) (Math.PI / stack);//单位角度值
        float sliceStep = (float) (Math.PI / slice);//水平圆递增的角度
        float r0, r1, x0, x1, y0, y1, z0, z1; //r0r1为圆心引向两个临近切片部分表面的两条线 (x0,y0,z0)(x1,y1,z1)为临近两个切面的点。
        float alpha0 = 0, alpha1 = 0; //前后两个角度
        float beta = 0; //切片平面上的角度
        List<Float> coordsList = new ArrayList<Float>();
        //外层循环
        for (int i = 0; i < stack; i++) {
            alpha0 = (float) (-Math.PI / 2 + (i * statckStep));
            alpha1 = (float) (-Math.PI / 2 + ((i + 1) * statckStep));
            y0 = (float) (r * Math.sin(alpha0));
            r0 = (float) (r * Math.cos(alpha0));
            y1 = (float) (r * Math.sin(alpha1));
            r1 = (float) (r * Math.cos(alpha1));

            //循环每一层圆
            for (int j = 0; j <= (slice * 2); j++) {
                beta = j * sliceStep;
                x0 = (float) (r0 * Math.cos(beta));
                z0 = -(float) (r0 * Math.sin(beta));
                x1 = (float) (r1 * Math.cos(beta));
                z1 = -(float) (r1 * Math.sin(beta));
                coordsList.add(x0);
                coordsList.add(y0);
                coordsList.add(z0);
                coordsList.add(x1);
                coordsList.add(y1);
                coordsList.add(z1);

            }
            //指定顶点指针
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtils.list2FloatBuffer(coordsList));
            //若要画球体,应以画三角形带的方式绘制,为方便观察,此处用画线带的方式
            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, coordsList.size() / 3);

        }
    }
    public static void drawRec(GL10 gl,float r) {

        float [] coords = {
                -r,r,0,
                -r,-r,0,
                r,r,0,
                r,-r,0,
        };

        gl.glVertexPointer(3,GL10.GL_FLOAT,0, BufferUtils.array2ByteBuffer(coords));//指定顶点指针

        gl.glDrawArrays( GL10.GL_TRIANGLE_STRIP,0,4 );

    }
}
父类:

public abstract class AbstractRenderer implements GLSurfaceView.Renderer{
    public float ratio;//视口比例
    public float xRotate = 0f;//X轴旋转的角度
    public float yRotate = 0f;//Y轴旋转的角度

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        /**
         * 步骤:
         * 第一步设置清屏色,第二步启用顶点缓冲数组。
         */

        //设置清屏色
        gl.glClearColor(0f, 0f, 0f, 1f);
        //启用顶点缓冲数组
        gl.glEnableClientState( GL10.GL_VERTEX_ARRAY );

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {

        /**步骤:
         * 第一步设置视口,再设置平截头体,而要设置平截头体就要先指定矩阵类型为投影矩阵
         * 设置过矩阵类型后,就马上要加载单位矩阵。
         *
         * 设置平截头体:
         * 为了视口所展示的画面不失真,一般设置平截头体的比例与视口比例相同。
         * 所以就要先计算视口比例,再应用到平截头体中。
         */

        //设置视口
        gl.glViewport(0,0,width,height);
        //计算视口比例
        ratio = (float)width / (float)height;
        //设置矩阵模式
        gl.glMatrixMode(GL10.GL_PROJECTION);
        //加载单位矩阵
        gl.glLoadIdentity();
        //设置平截头体
        gl.glFrustumf(ratio,-ratio,-1f,1f,3f,7f);//ratiowith/height,所以高度为1,宽度为ratio

    }

    @Override
    public abstract void onDrawFrame(GL10 gl);
    //光照
    public boolean open_lighting = false;
    //全局环境光
    public float r = 0.2f;
    public float g = 0.2f;
    public float b = 0.2f;
    public float a = 1;
    //材料的环境光和散射光的反射率
    public float r1 = 0;
    public float g1 = 0;
    public float b1 = 0;
    public float a1 = 1;
    public boolean open_ColorMatrial = false;
    //颜色追踪
    public float color_r = 0.8f;
    public float color_g = 0.8f;
    public float color_b = 0.8f;
    public float color_a = 1;
    //是否启用光源0
    public boolean open_light0;
    //光源0的环境光
    public float r3 = 0;
    public float g3 = 0;
    public float b3 = 0;
    public float a3 = 1;
    //光源0的散射光
    public float r4 = 0;
    public float g4 = 0;
    public float b4 = 0;
    public float a4 = 1;
    //光源0位置
    public float x = 0;
    public float y = 1f;
    public float z = 10f;
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <CheckBox
        android:id="@+id/cb_open_lighting"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启用光照"/>


    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="---------------------------------------全局环境光---------------------------------------"/>
    <SeekBar
        android:id="@+id/sb_r"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_g"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_b"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="-------------材料的环境光和散射光的反射率(相同的值)------------"/>
    <SeekBar
        android:id="@+id/sb_r1"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_g1"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_b1"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>

    <CheckBox
        android:id="@+id/cb_open_ColorMatrial"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启用颜色追踪(设置材料反射率)"/>
    <SeekBar
        android:id="@+id/sb_r2"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_g2"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_b2"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <CheckBox
        android:id="@+id/cb_open_light0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启用光源0"/>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="---------------------------------------光源0环境光---------------------------------------"/>
    <SeekBar
        android:id="@+id/sb_r3_ambient"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_g3_ambient"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_b3_ambient"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="---------------------------------------光源0散射光---------------------------------------"/>
    <SeekBar
        android:id="@+id/sb_r3_diffuse"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_g3_diffuse"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_b3_diffuse"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="---------------------------------------光源0位置(xyz轴)---------------------------------------"/>
    <SeekBar
        android:id="@+id/sb_x"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_y"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>
    <SeekBar
        android:id="@+id/sb_z"
        android:layout_width="200dip"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:max="100"/>

</LinearLayout>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值