OpenGL十二讲 源码 结果

第一讲

#include <GL/glut.h>

void myDisplay(void)

{

    glClear(GL_COLOR_BUFFER_BIT);

    glRectf(-0.5f, -0.5f, 0.5f, 0.5f);

    glFlush();

}

int main(int argc, char *argv[])

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

    glutInitWindowPosition(100, 100);

    glutInitWindowSize(400, 400);

    glutCreateWindow("第一个OpenGL程序");

    glutDisplayFunc(&myDisplay);

    glutMainLoop();

    return 0;

}

第二讲

#include <GL/glut.h>

#include <math.h>

GLfloat Pi = 3.1415926536f;

void myDisplay(void)

{

    GLfloat a = 1 / (2-2*cos(72*Pi/180));

    GLfloat bx = a * cos(18 * Pi/180);

    GLfloat by = a * sin(18 * Pi/180);

    GLfloat cy = -a * cos(18 * Pi/180);

    GLfloat

        PointA[2] = { 0, a },

        PointB[2] = { bx, by },

        PointC[2] = { 0.5, cy },

        PointD[2] = { -0.5, cy },

        PointE[2] = { -bx, by };

    glClear(GL_COLOR_BUFFER_BIT);

    // 按照A->C->E->B->D->A的顺序,可以一笔将五角星画出

    glBegin(GL_LINE_LOOP);

        glVertex2fv(PointA);

        glVertex2fv(PointC);

        glVertex2fv(PointE);

        glVertex2fv(PointB);

        glVertex2fv(PointD);

    glEnd();

    glFlush();

}

int main(int argc, char *argv[])

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

    glutInitWindowPosition(100, 100);

    glutInitWindowSize(400, 400);

    glutCreateWindow("第一个OpenGL程序");

    glutDisplayFunc(&myDisplay);

    glutMainLoop();

    return 0;

}

第三讲

#include <GL/glut.h>

void myDisplay(void)

{

    glClear(GL_COLOR_BUFFER_BIT);

    glEnable(GL_LINE_STIPPLE);

    glLineStipple(2, 0x0F0F);

    glLineWidth(10.0f);

    glBegin(GL_LINES);

        glVertex2f(0.0f, 0.0f);

        glVertex2f(0.5f, 0.5f);

    glEnd();

    glFlush();

}

int main(int argc, char *argv[])

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

    glutInitWindowPosition(100, 100);

    glutInitWindowSize(400, 400);

    glutCreateWindow("第一个OpenGL程序");

    glutDisplayFunc(&myDisplay);

    glutMainLoop();

    return 0;

}

第四讲

void myDisplay(void)

{

    int i;

    for(i=0; i<8; ++i)

        auxSetOneColor(i, (float)(i&0x04), (float)(i&0x02), (float)(i&0x01));

    glShadeModel(GL_FLAT);

    glClear(GL_COLOR_BUFFER_BIT);

    glBegin(GL_TRIANGLE_FAN);

    glVertex2f(0.0f, 0.0f);

    for(i=0; i<=8; ++i)

    {

        glIndexi(i);

        glVertex2f(cos(i*Pi/4), sin(i*Pi/4));

    }

    glEnd();

    glFlush();

}

int main(void)

{

    auxInitDisplayMode(AUX_SINGLE|AUX_INDEX);

    auxInitPosition(0, 0, 400, 400);

    auxInitWindow(L"");

    myDisplay();

    Sleep(10 * 1000);

    return 0;

}

第五讲

#include <stdio.h>

#include <math.h>

#include <Windows.h>

#include <Gl/glut.h>

int day = 200; // day的变化:从到

void myDisplay(void)

{       

        glEnable(GL_BLEND);// 深度测试

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清空深度缓冲

    glMatrixMode(GL_PROJECTION);//投影矩阵

    glLoadIdentity();//当前矩阵设置为单位矩阵

    gluPerspective(60, 1, 1, 400000000);//设置可视空间可视角高宽比最近可视空间最远可视空间

    glMatrixMode(GL_MODELVIEW);//模型视图矩阵

    glLoadIdentity();

        //前三个参数表示了观察点的位置,中间三个参数表示了观察目标的位置,最后三个参数代表从(0,0,0)到(x,y,z)的直线,它表示了观察者认为的方向。

    gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);

    // 绘制红色的太阳

    glColor3f(1.0f, 0.0f, 0.0f);//画笔颜色

    glutSolidSphere(69600000, 20, 20);//绘制球体半径面的数目

    // 绘制蓝色的地球

    glColor3f(0.0f, 0.0f, 1.0f);

    glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);

    glTranslatef(150000000, 0.0f, 0.0f);

    glutSolidSphere(15945000, 20, 20);

    // 绘制黄色的月亮

    glColor3f(1.0f, 1.0f, 0.0f);

    glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);

    glTranslatef(38000000, 0.0f, 0.0f);

    glutSolidSphere(4345000, 20, 20);

    glFlush();

}

int main(int argc, char *argv[])

{

    glutInit(&argc, argv);//GLUT初始化使用前调用

    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);//显示方式GLUT-RGB表示RGB颜色GLUT_SINGLE单缓冲

    glutInitWindowPosition(100, 100);// 设置窗口在屏幕中的位置

    glutInitWindowSize(400, 400);// 设置窗口的大小长宽

    glutCreateWindow("OpenGL");// 窗口的标题

    glutDisplayFunc(&myDisplay);//需要进行画图时,这个函数就会被调用

    glutMainLoop();//保持窗口显示

    return 0;

}

第六讲

#include <stdio.h>

#include <math.h>

#include <Windows.h>

#include <Gl/glut.h>

int day = 0; // day的变化:从到

void myDisplay(void)

{       

    glEnable(GL_BLEND);// 深度测试

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清空深度缓冲

    glMatrixMode(GL_PROJECTION);//投影矩阵

    glLoadIdentity();//当前矩阵设置为单位矩阵

    gluPerspective(60, 1, 1, 400000000);//设置可视空间可视角高宽比最近可视空间最远可视空间

    glMatrixMode(GL_MODELVIEW);//模型视图矩阵

    glLoadIdentity();

        //前三个参数表示了观察点的位置,中间三个参数表示了观察目标的位置,最后三个参数代表从(0,0,0)到(x,y,z)的直线,它表示了观察者认为的方向。

    gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);

    // 绘制红色的太阳

    glColor3f(1.0f, 0.0f, 0.0f);//画笔颜色

    glutSolidSphere(69600000, 20, 20);//绘制球体半径面的数目

    // 绘制蓝色的地球

    glColor3f(0.0f, 0.0f, 1.0f);

    glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);

    glTranslatef(150000000, 0.0f, 0.0f);

    glutSolidSphere(15945000, 20, 20);

    // 绘制黄色的月亮

    glColor3f(1.0f, 1.0f, 0.0f);

    glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);

    glTranslatef(38000000, 0.0f, 0.0f);

    glutSolidSphere(4345000, 20, 20);

    glutSwapBuffers();

}

void OnTimer(int value)

{

        //改变角度

        day += 1;

        day%=360;

    glutPostRedisplay();//重画

        glutTimerFunc(16, OnTimer, 1);

}

int main(int argc, char *argv[])

{

    glutInit(&argc, argv);//GLUT初始化使用前调用

    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);//显示方式GLUT-RGB表示RGB颜色GLUT_SINGLE单缓冲

    glutInitWindowPosition(100, 100);// 设置窗口在屏幕中的位置

    glutInitWindowSize(400, 400);// 设置窗口的大小长宽

    glutCreateWindow("OpenGL");// 窗口的标题

    glutDisplayFunc(&myDisplay);//需要进行画图时,这个函数就会被调用

    glutTimerFunc(16, OnTimer, 1);//计时器定时运行FPS

    glutMainLoop();//保持窗口显示

    return 0;

}

第七讲

include <gl/glut.h>

#define WIDTH 400

#define HEIGHT 400

static GLfloat angle = 0.0f;

void myDisplay(void)

{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 创建透视效果视图

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();

    gluPerspective(90.0f, 1.0f, 1.0f, 20.0f);

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

    gluLookAt(0.0, 5.0, -10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

    // 定义太阳光源,它是一种白色的光源

    {

    GLfloat sun_light_position[] = {0.0f, 0.0f, 0.0f, 1.0f};

    GLfloat sun_light_ambient[]  = {0.0f, 0.0f, 0.0f, 1.0f};

    GLfloat sun_light_diffuse[]  = {1.0f, 1.0f, 1.0f, 1.0f};

    GLfloat sun_light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};

    glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);

    glLightfv(GL_LIGHT0, GL_AMBIENT,  sun_light_ambient);

    glLightfv(GL_LIGHT0, GL_DIFFUSE,  sun_light_diffuse);

    glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);

    glEnable(GL_LIGHT0);

    glEnable(GL_LIGHTING);

    glEnable(GL_DEPTH_TEST);

    }

    // 定义太阳的材质并绘制太阳

    {

        GLfloat sun_mat_ambient[]  = {0.0f, 0.0f, 0.0f, 1.0f};

        GLfloat sun_mat_diffuse[]  = {0.0f, 0.0f, 0.0f, 1.0f};

        GLfloat sun_mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};

        GLfloat sun_mat_emission[] = {0.5f, 0.0f, 0.0f, 1.0f};

        GLfloat sun_mat_shininess  = 0.0f;

        glMaterialfv(GL_FRONT, GL_AMBIENT,   sun_mat_ambient);

        glMaterialfv(GL_FRONT, GL_DIFFUSE,   sun_mat_diffuse);

        glMaterialfv(GL_FRONT, GL_SPECULAR,  sun_mat_specular);

        glMaterialfv(GL_FRONT, GL_EMISSION,  sun_mat_emission);

        glMaterialf (GL_FRONT, GL_SHININESS, sun_mat_shininess);

        glutSolidSphere(2.0, 40, 32);

    }

    // 定义地球的材质并绘制地球

    {

        GLfloat earth_mat_ambient[]  = {0.0f, 0.0f, 0.5f, 1.0f};

        GLfloat earth_mat_diffuse[]  = {0.0f, 0.0f, 0.5f, 1.0f};

        GLfloat earth_mat_specular[] = {0.0f, 0.0f, 1.0f, 1.0f};

        GLfloat earth_mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};

        GLfloat earth_mat_shininess  = 30.0f;

        glMaterialfv(GL_FRONT, GL_AMBIENT,   earth_mat_ambient);

        glMaterialfv(GL_FRONT, GL_DIFFUSE,   earth_mat_diffuse);

        glMaterialfv(GL_FRONT, GL_SPECULAR,  earth_mat_specular);

        glMaterialfv(GL_FRONT, GL_EMISSION,  earth_mat_emission);

        glMaterialf (GL_FRONT, GL_SHININESS, earth_mat_shininess);

        glRotatef(angle, 0.0f, -1.0f, 0.0f);

        glTranslatef(5.0f, 0.0f, 0.0f);

        glutSolidSphere(2.0, 40, 32);

    }

    glutSwapBuffers();

}

void myIdle(void)

{

    angle += 1.0f;

    if( angle >= 360.0f )

        angle = 0.0f;

    myDisplay();

}

int main(int argc, char* argv[])

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

    glutInitWindowPosition(200, 200);

    glutInitWindowSize(WIDTH, HEIGHT);

    glutCreateWindow("OpenGL光照演示");

    glutDisplayFunc(&myDisplay);

    glutIdleFunc(&myIdle);

    glutMainLoop();

    return 0;

}

第八讲

#include <gl/glut.h>

#define WIDTH 400

#define HEIGHT 400

#include <math.h>

#define ColoredVertex(c, v) do{ glColor3fv(c); glVertex3fv(v); }while(0)

GLfloat angle = 0.0f;

void myDisplay(void)

{

    static int list = 0;

    if( list == 0 )

    {

        // 如果显示列表不存在,则创建

        /* GLfloat

            PointA[] = {-0.5, -5*sqrt(5)/48,  sqrt(3)/6},

            PointB[] = { 0.5, -5*sqrt(5)/48,  sqrt(3)/6},

            PointC[] = {   0, -5*sqrt(5)/48, -sqrt(3)/3},

            PointD[] = {   0, 11*sqrt(6)/48,          0}; */

        // 2007年月日修改

        GLfloat

            PointA[] = { 0.5f, -sqrt(6.0f)/12, -sqrt(3.0f)/6},

            PointB[] = {-0.5f, -sqrt(6.0f)/12, -sqrt(3.0f)/6},

            PointC[] = { 0.0f, -sqrt(6.0f)/12,  sqrt(3.0f)/3},

            PointD[] = { 0.0f,   sqrt(6.0f)/4,             0};

        GLfloat

            ColorR[] = {1, 0, 0},

            ColorG[] = {0, 1, 0},

            ColorB[] = {0, 0, 1},

            ColorY[] = {1, 1, 0};

        list = glGenLists(1);

        glNewList(list, GL_COMPILE);

        glBegin(GL_TRIANGLES);

        // 平面ABC

        ColoredVertex(ColorR, PointA);

        ColoredVertex(ColorG, PointB);

        ColoredVertex(ColorB, PointC);

        // 平面ACD

        ColoredVertex(ColorR, PointA);

        ColoredVertex(ColorB, PointC);

        ColoredVertex(ColorY, PointD);

        // 平面CBD

        ColoredVertex(ColorB, PointC);

        ColoredVertex(ColorG, PointB);

        ColoredVertex(ColorY, PointD);

        // 平面BAD

        ColoredVertex(ColorG, PointB);

        ColoredVertex(ColorR, PointA);

        ColoredVertex(ColorY, PointD);

        glEnd();

        glEndList();

        glEnable(GL_DEPTH_TEST);

    }

    // 已经创建了显示列表,在每次绘制正四面体时将调用它

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

    glRotatef(angle, 1, 0.5, 0);

    glCallList(list);

    glPopMatrix();

    glutSwapBuffers();

}

void myIdle(void)

{

    ++angle;

    if( angle >= 360.0f )

        angle = 0.0f;

    myDisplay();

}

int main(int argc, char* argv[])

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

    glutInitWindowPosition(200, 200);

    glutInitWindowSize(WIDTH, HEIGHT);

    glutCreateWindow("OpenGL 窗口");

    glutDisplayFunc(&myDisplay);

    glutIdleFunc(&myIdle);

    glutMainLoop();

    return 0;

}

第九讲

#include <stdio.h>

#include <math.h>

#include <Windows.h>

#include <Gl/glut.h>

#define WIDTH 400

#define HEIGHT 400

void setLight(void)

{

    static const GLfloat light_position[] = {1.0f, 1.0f, -1.0f, 1.0f};

    static const GLfloat light_ambient[]  = {0.2f, 0.2f, 0.2f, 1.0f};

    static const GLfloat light_diffuse[]  = {1.0f, 1.0f, 1.0f, 1.0f};

    static const GLfloat light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};

    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_LIGHT0);

    glEnable(GL_LIGHTING);

    glEnable(GL_DEPTH_TEST);

}

// 每一个球体颜色不同。所以它们的材质也都不同。这里用一个函数来设置材质。

void setMatirial(const GLfloat mat_diffuse[4], GLfloat mat_shininess)

{

    static const GLfloat mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};

    static const GLfloat mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};

    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_diffuse);

    glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);

    glMaterialfv(GL_FRONT, GL_EMISSION,  mat_emission);

    glMaterialf (GL_FRONT, GL_SHININESS, mat_shininess);

}

// 有了这两个函数,我们就可以根据前面的知识写出整个程序代码了。这里只给出了绘制的部分,其它部分大家可以自行完成。

void myDisplay(void)

{

    // 定义一些材质颜色

    const static GLfloat red_color[] = {1.0f, 0.0f, 0.0f, 1.0f};

    const static GLfloat green_color[] = {0.0f, 1.0f, 0.0f, 0.3333f};

    const static GLfloat blue_color[] = {0.0f, 0.0f, 1.0f, 0.5f};

    // 清除屏幕

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 启动混合并设置混合因子

    glEnable(GL_BLEND);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // 设置光源

    setLight();

    // 以(0, 0, 0.5)为中心,绘制一个半径为.3的不透明红色球体(离观察者最远)

    setMatirial(red_color, 30.0);

    glPushMatrix();

    glTranslatef(0.0f, 0.0f, 0.5f);

    glutSolidSphere(0.3, 30, 30);

    glPopMatrix();

    // 下面将绘制半透明物体了,因此将深度缓冲设置为只读

    glDepthMask(GL_FALSE);

    // 以(0.2, 0, -0.5)为中心,绘制一个半径为.2的半透明蓝色球体(离观察者最近)

    setMatirial(blue_color, 30.0);

    glPushMatrix();

    glTranslatef(0.2f, 0.0f, -0.5f);

    glutSolidSphere(0.2, 30, 30);//第一个参数表示球体的半径,后两个参数代表了的数目,简单点说就是球体的精确程度,数值越大越精确,当然代价就是速度越缓慢

    glPopMatrix();

    // 以(0.1, 0, 0)为中心,绘制一个半径为.15的半透明绿色球体(在前两个球体之间)

    setMatirial(green_color, 30.0);

    glPushMatrix();

    glTranslatef(0.1, 0, 0);

    glutSolidSphere(0.15, 30, 30);

    glPopMatrix();

    // 完成半透明物体的绘制,将深度缓冲区恢复为可读可写的形式

    glDepthMask(GL_TRUE);

    glutSwapBuffers();

}

int main(int argc, char* argv[])

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

    glutInitWindowPosition(200, 200);

    glutInitWindowSize(WIDTH, HEIGHT);

    glutCreateWindow("OpenGL 窗口");

    glutDisplayFunc(&myDisplay);

    glutMainLoop();

    return 0;

}

第十讲

#include <math.h>

#include <Windows.h>

#include <Gl/glut.h>

#include <stdio.h>

#include <stdlib.h>

#define WindowWidth  400

#define WindowHeight 400

/* 函数grab

 * 抓取窗口中的像素

 * 假设窗口宽度为WindowWidth,高度为WindowHeight

 */

#define BMP_Header_Length 54

void grab(void)

{

    FILE*    pDummyFile;

    FILE*    pWritingFile;

    GLubyte* pPixelData;

    GLubyte  BMP_Header[BMP_Header_Length];

    GLint    i, j;

    GLint    PixelDataLength;

    // 计算像素数据的实际长度

    i = WindowWidth * 3;   // 得到每一行的像素数据长度

    while( i%4 != 0 )      // 补充数据,直到i是的倍数

        ++i;               // 本来还有更快的算法,

                           // 但这里仅追求直观,对速度没有太高要求

    PixelDataLength = i * WindowHeight;

    // 分配内存和打开文件

    pPixelData = (GLubyte*)malloc(PixelDataLength);

    if( pPixelData == 0 )

        exit(0);

    pDummyFile = fopen("dummy.bmp", "rb");

    if( pDummyFile == 0 )

        exit(0);

    pWritingFile = fopen("grab.bmp", "wb");

    if( pWritingFile == 0 )

        exit(0);

    // 读取像素

    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    glReadPixels(0, 0, WindowWidth, WindowHeight,

        GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);

    // 把dummy.bmp的文件头复制为新文件的文件头

    fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile);

    fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);

    fseek(pWritingFile, 0x0012, SEEK_SET);

    i = WindowWidth;

    j = WindowHeight;

    fwrite(&i, sizeof(i), 1, pWritingFile);

    fwrite(&j, sizeof(j), 1, pWritingFile);

    // 写入像素数据

    fseek(pWritingFile, 0, SEEK_END);

    fwrite(pPixelData, PixelDataLength, 1, pWritingFile);

    // 释放内存和关闭文件

    fclose(pDummyFile);

    fclose(pWritingFile);

    free(pPixelData);

}

void display(void)

{

    // 清除屏幕

    glClear(GL_COLOR_BUFFER_BIT);

    // 绘制

    glBegin(GL_TRIANGLES);

        glColor3f(1.0f, 0.0f, 0.0f);    glVertex2f(0.0f, 0.0f);

        glColor3f(0.0f, 1.0f, 0.0f);    glVertex2f(1.0f, 0.0f);

        glColor3f(0.0f, 0.0f, 1.0f);    glVertex2f(0.5f, 1.0f);

    glEnd();

    glPixelZoom(-0.5f, -0.5f);

    glRasterPos2i(1, 1);

    glCopyPixels(WindowWidth/2, WindowHeight/2,

        WindowWidth/2, WindowHeight/2, GL_COLOR);

    // 完成绘制,并抓取图象保存为BMP文件

    glutSwapBuffers();

    grab();

}

int main(int argc, char* argv[])

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

    glutInitWindowPosition(200, 200);

    glutInitWindowSize(WindowWidth, WindowHeight);

    glutCreateWindow("OpenGL 窗口");

    glutDisplayFunc(&display);

    glutMainLoop();

    return 0;

}

第十一讲

#include<stdio.h>

#include<stdlib.h>

#include <math.h>

#include <Gl/glut.h>

#include<glaux.h>

#include <time.h>

#define WindowWidth   500

#define WindowHeight 500

/* 函数grab

 * 抓取窗口中的像素

 * 假设窗口宽度为WindowWidth,高度为WindowHeight

 */

#define BMP_Header_Length 54

void grab(void)

{

    FILE* pDummyFile;

    FILE* pWritingFile;

    GLubyte* pPixelData;

    GLubyte  BMP_Header[BMP_Header_Length];

    GLint    i, j;

    GLint    PixelDataLength;

    // 计算像素数据的实际长度

    i = WindowWidth * 3;   // 得到每一行的像素数据长度

    while (i % 4 != 0)      // 补充数据,直到i是的倍数

        ++i;               // 本来还有更快的算法,

                           // 但这里仅追求直观,对速度没有太高要求

    PixelDataLength = i * WindowHeight;

    // 分配内存和打开文件

    pPixelData = (GLubyte*)malloc(PixelDataLength);

    if (pPixelData == 0)

        exit(0);

    fopen_s(&pDummyFile, "dummy.bmp", "rb");

    if (pDummyFile == 0)

        exit(0);

    fopen_s(&pWritingFile, "grab.bmp", "wb");

    if (pWritingFile == 0)

        exit(0);

    // 读取像素

    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    glReadPixels(0, 0, WindowWidth, WindowHeight,

        GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);

    // 把dummy.bmp的文件头复制为新文件的文件头

    fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile);

    fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);

    fseek(pWritingFile, 0x0012, SEEK_SET);

    i = WindowWidth;

    j = WindowHeight;

    fwrite(&i, sizeof(i), 1, pWritingFile);

    fwrite(&j, sizeof(j), 1, pWritingFile);

    // 写入像素数据

    fseek(pWritingFile, 0, SEEK_END);

    fwrite(pPixelData, PixelDataLength, 1, pWritingFile);

    // 释放内存和关闭文件

    fclose(pDummyFile);

    fclose(pWritingFile);

    free(pPixelData);

}

/* 函数power_of_two

 * 检查一个整数是否为的整数次方,如果是,返回,否则返回

 * 实际上只要查看其二进制位中有多少个,如果正好有个,返回,否则返回

 * 查看其二进制位中有多少个时使用了一个小技巧

 * 使用n &= (n-1)可以使得n中的减少一个(具体原理大家可以自己思考)

 */

int power_of_two(int n)

{

    if (n <= 0)

        return 0;

    return (n & (n - 1)) == 0;

}

/* 函数load_texture

 * 读取一个BMP文件作为纹理

 * 如果失败,返回,如果成功,返回纹理编号

 */

GLuint load_texture(const char* file_name)

{

    GLint width, height, total_bytes;

    GLubyte* pixels = 0;

    GLuint last_texture_ID, texture_ID = 0;

    // 打开文件,如果失败,返回

    FILE* pFile;

    fopen_s(&pFile, file_name, "rb");

    if (pFile == 0)

        return 0;

    // 读取文件中图象的宽度和高度

    fseek(pFile, 0x0012, SEEK_SET);

    fread(&width, 4, 1, pFile);

    fread(&height, 4, 1, pFile);

    fseek(pFile, BMP_Header_Length, SEEK_SET);

    // 计算每行像素所占字节数,并根据此数据计算总像素字节数

    {

        GLint line_bytes = width * 3;

        while (line_bytes % 4 != 0)

            ++line_bytes;

        total_bytes = line_bytes * height;

    }

    // 根据总像素字节数分配内存

    pixels = (GLubyte*)malloc(total_bytes);

    if (pixels == 0)

    {

        fclose(pFile);

        return 0;

    }

    // 读取像素数据

    if (fread(pixels, total_bytes, 1, pFile) <= 0)

    {

        free(pixels);

        fclose(pFile);

        return 0;

    }

    // 在旧版本的OpenGL中

    // 如果图象的宽度和高度不是的整数次方,则需要进行缩放

    // 这里并没有检查OpenGL版本,出于对版本兼容性的考虑,按旧版本处理

    // 另外,无论是旧版本还是新版本,

    // 当图象的宽度和高度超过当前OpenGL实现所支持的最大值时,也要进行缩放

    {

        GLint max;

        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);

        if (!power_of_two(width)

            || !power_of_two(height)

            || width > max

            || height > max)

        {

            const GLint new_width = 256;

            const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形

            GLint new_line_bytes, new_total_bytes;

            GLubyte* new_pixels = 0;

            // 计算每行需要的字节数和总字节数

            new_line_bytes = new_width * 3;

            while (new_line_bytes % 4 != 0)

                ++new_line_bytes;

            new_total_bytes = new_line_bytes * new_height;

            // 分配内存

            new_pixels = (GLubyte*)malloc(new_total_bytes);

            if (new_pixels == 0)

            {

                free(pixels);

                fclose(pFile);

                return 0;

            }

            // 进行像素缩放

            gluScaleImage(GL_RGB,

                width, height, GL_UNSIGNED_BYTE, pixels,

                new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);

            // 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height

            free(pixels);

            pixels = new_pixels;

            width = new_width;

            height = new_height;

        }

    }

    // 分配一个新的纹理编号

    glGenTextures(1, &texture_ID);

    if (texture_ID == 0)

    {

        free(pixels);

        fclose(pFile);

        return 0;

    }

    // 绑定新的纹理,载入纹理并设置纹理参数

    // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复

    glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture_ID);

    glBindTexture(GL_TEXTURE_2D, texture_ID);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,

        GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);//利用glTexImage2D函数可以载入一个二维的纹理

    glBindTexture(GL_TEXTURE_2D, last_texture_ID);//指定当前所使用的纹理对象

    // 之前为pixels分配的内存可在使用glTexImage2D以后释放

    // 因为此时像素数据已经被OpenGL另行保存了一份(可能被保存到专门的图形硬件中)

    free(pixels);

    return texture_ID;

}

/* 两个纹理对象的编号

 */

GLuint texGround;

GLuint texWall;

void display(void)

{

    // 清除屏幕

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 设置视角

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();

    gluPerspective(75, 1, 1, 21);

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

    gluLookAt(1, 5, 5, 0, 0, 0, 0, 0, 1);

    // 使用纹理绘制土地

    glBindTexture(GL_TEXTURE_2D, texGround);

    glBegin(GL_QUADS);//QUADS指的是四边形,

    glTexCoord2f(0.0f, 0.0f); glVertex3f(-8.0f, -8.0f, 0.0f);

    glTexCoord2f(0.0f, 5.0f); glVertex3f(-8.0f, 8.0f, 0.0f);

    glTexCoord2f(5.0f, 5.0f); glVertex3f(8.0f, 8.0f, 0.0f);

    glTexCoord2f(5.0f, 0.0f); glVertex3f(8.0f, -8.0f, 0.0f);

    glEnd();

    // 使用纹理绘制栅栏

    glBindTexture(GL_TEXTURE_2D, texWall);

    glBegin(GL_QUADS);

    glTexCoord2f(0.0f, 0.0f); glVertex3f(-6.0f, -3.0f, 0.0f);

    glTexCoord2f(0.0f, 1.0f); glVertex3f(-6.0f, -3.0f, 1.5f);

    glTexCoord2f(5.0f, 1.0f); glVertex3f(6.0f, -3.0f, 1.5f);

    glTexCoord2f(5.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f);

    glEnd();

    // 旋转后再绘制一个

    glRotatef(-90, 0, 0, 1);

    glBegin(GL_QUADS);

    glTexCoord2f(0.0f, 0.0f); glVertex3f(-6.0f, -3.0f, 0.0f);

    glTexCoord2f(0.0f, 1.0f); glVertex3f(-6.0f, -3.0f, 1.5f);

    glTexCoord2f(5.0f, 1.0f); glVertex3f(6.0f, -3.0f, 1.5f);

    glTexCoord2f(5.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f);

    glEnd();

    grab();

    // 交换缓冲区,并保存像素数据到文件

    glutSwapBuffers();

}

int main(int argc, char* argv[])

{

    // GLUT初始化

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);

    glutInitWindowPosition(100, 100);

    glutInitWindowSize(WindowWidth, WindowHeight);

    glutCreateWindow("纹理映射测试");

    glutDisplayFunc(&display);

    // 在这里做一些初始化

    glEnable(GL_DEPTH_TEST);

    glEnable(GL_TEXTURE_2D);

    texGround = load_texture("ground.bmp");

    printf("%d", (int)texGround);

    texWall = load_texture("wall.bmp");

    printf("%d", (int)texWall);

    // 开始显示

    glutMainLoop();

    return 0;

}

第十二讲-1

#include <gl/glut.h>

#include <stdio.h>

#include <stdlib.h>

#define WindowWidth  400

#define WindowHeight 400

#define WindowTitle  "OpenGL模板测试"

#define BMP_Header_Length 54

void grab(void)

{

    FILE* pDummyFile;

    FILE* pWritingFile;

    GLubyte* pPixelData;

    GLubyte  BMP_Header[BMP_Header_Length];

    GLint    i, j;

    GLint    PixelDataLength;

    // 计算像素数据的实际长度

    i = WindowWidth * 3;   // 得到每一行的像素数据长度

    while (i % 4 != 0)      // 补充数据,直到i是的倍数

        ++i;               // 本来还有更快的算法,

                           // 但这里仅追求直观,对速度没有太高要求

    PixelDataLength = i * WindowHeight;

    // 分配内存和打开文件

    pPixelData = (GLubyte*)malloc(PixelDataLength);

    if (pPixelData == 0)

        exit(0);

    pDummyFile = fopen("dummy.bmp", "rb");

    if (pDummyFile == 0)

        exit(0);

    pWritingFile = fopen("grab.bmp", "wb");

    if (pWritingFile == 0)

        exit(0);

    // 读取像素

    glReadBuffer(GL_FRONT);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    glReadPixels(0, 0, WindowWidth, WindowHeight,

        GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);

    // 把dummy.bmp的文件头复制为新文件的文件头

    fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile);

    fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);

    fseek(pWritingFile, 0x0012, SEEK_SET);

    i = WindowWidth;

    j = WindowHeight;

    fwrite(&i, sizeof(i), 1, pWritingFile);

    fwrite(&j, sizeof(j), 1, pWritingFile);

    // 写入像素数据

    fseek(pWritingFile, 0, SEEK_END);

    fwrite(pPixelData, PixelDataLength, 1, pWritingFile);

    // 释放内存和关闭文件

    fclose(pDummyFile);

    fclose(pWritingFile);

    free(pPixelData);

}

void draw_sphere()

{

    // 设置光源

    glEnable(GL_LIGHTING);

    glEnable(GL_LIGHT0);

    {

        GLfloat

            pos[] = { 5.0f, 5.0f, 0.0f, 1.0f },

            ambient[] = { 0.0f, 0.0f, 1.0f, 1.0f };

        glLightfv(GL_LIGHT0, GL_POSITION, pos);

        glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);

    }

    // 绘制一个球体

    glColor3f(1, 0, 0);

    glPushMatrix();

    glTranslatef(0, 0, 2);

    glutSolidSphere(0.5, 20, 20);

    glPopMatrix();

}

void display(void)

{

    // 清除屏幕

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 设置观察点

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();

    gluPerspective(60, 1, 5, 25);

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

    gluLookAt(5, 0, 6.5, 0, 0, 0, 0, 1, 0);

    glEnable(GL_DEPTH_TEST);

    // 绘制球体

    glDisable(GL_STENCIL_TEST);

    draw_sphere();

    // 绘制一个平面镜。在绘制的同时注意设置模板缓冲。

    // 另外,为了保证平面镜之后的镜像能够正确绘制,在绘制平面镜时需要将深度缓冲区设置为只读的。

    // 在绘制时暂时关闭光照效果

    glClearStencil(0);

    glClear(GL_STENCIL_BUFFER_BIT);

    glStencilFunc(GL_ALWAYS, 1, 0xFF);

    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

    glEnable(GL_STENCIL_TEST);

    glDisable(GL_LIGHTING);

    glColor3f(0.5f, 0.5f, 0.5f);

    glDepthMask(GL_FALSE);

    glRectf(-1.5f, -1.5f, 1.5f, 1.5f);

    glDepthMask(GL_TRUE);

    // 绘制一个与先前球体关于平面镜对称的球体,注意光源的位置也要发生对称改变

    // 因为平面镜是在X轴和Y轴所确定的平面,所以只要Z坐标取反即可实现对称

    // 为了保证球体的绘制范围被限制在平面镜内部,使用模板测试

    glStencilFunc(GL_EQUAL, 1, 0xFF);

    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

    glScalef(1.0f, 1.0f, -1.0f);

    draw_sphere();

    // 交换缓冲

    glutSwapBuffers();

    // 截图

    grab();

}

int main(int argc, char* argv[])

{

    // GLUT初始化

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);

    glutInitWindowPosition(100, 100);

    glutInitWindowSize(WindowWidth, WindowHeight);

    glutCreateWindow(WindowTitle);

    glutDisplayFunc(&display);

    glutMainLoop();

return 0;

}

}

第十二讲-2

#include <gl/glut.h>

#include <stdio.h>

#include <stdlib.h>

#define WindowWidth  400

#define WindowHeight 400

#define WindowTitle  "OpenGLalpha测试"

GLuint texGround;

GLuint texWall;

#define BMP_Header_Length 54

/* 函数power_of_two

 * 检查一个整数是否为的整数次方,如果是,返回,否则返回

 * 实际上只要查看其二进制位中有多少个,如果正好有个,返回,否则返回

 * 查看其二进制位中有多少个时使用了一个小技巧

 * 使用n &= (n-1)可以使得n中的减少一个(具体原理大家可以自己思考)

 */

int power_of_two(int n)

{

    if (n <= 0)

        return 0;

    return (n & (n - 1)) == 0;

}

/* 函数load_texture

 * 读取一个BMP文件作为纹理

 * 如果失败,返回,如果成功,返回纹理编号

 */

GLuint load_texture(const char* file_name)

{

    GLint width, height, total_bytes;

    GLubyte* pixels = 0;

    GLuint last_texture_ID, texture_ID = 0;

    // 打开文件,如果失败,返回

    FILE* pFile = fopen(file_name, "rb");

    if (pFile == 0)

        return 0;

    // 读取文件中图象的宽度和高度

    fseek(pFile, 0x0012, SEEK_SET);

    fread(&width, 4, 1, pFile);

    fread(&height, 4, 1, pFile);

    fseek(pFile, BMP_Header_Length, SEEK_SET);

    // 计算每行像素所占字节数,并根据此数据计算总像素字节数

    {

        GLint line_bytes = width * 3;

        while (line_bytes % 4 != 0)

            ++line_bytes;

        total_bytes = line_bytes * height;

    }

    // 根据总像素字节数分配内存

    pixels = (GLubyte*)malloc(total_bytes);

    if (pixels == 0)

    {

        fclose(pFile);

        return 0;

    }

    // 读取像素数据

    if (fread(pixels, total_bytes, 1, pFile) <= 0)

    {

        free(pixels);

        fclose(pFile);

        return 0;

    }

    // 在旧版本的OpenGL中

    // 如果图象的宽度和高度不是的整数次方,则需要进行缩放

    // 这里并没有检查OpenGL版本,出于对版本兼容性的考虑,按旧版本处理

    // 另外,无论是旧版本还是新版本,

    // 当图象的宽度和高度超过当前OpenGL实现所支持的最大值时,也要进行缩放

    {

        GLint max;

        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);

        if (!power_of_two(width)

            || !power_of_two(height)

            || width > max

            || height > max)

        {

            const GLint new_width = 256;

            const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形

            GLint new_line_bytes, new_total_bytes;

            GLubyte* new_pixels = 0;

            // 计算每行需要的字节数和总字节数

            new_line_bytes = new_width * 3;

            while (new_line_bytes % 4 != 0)

                ++new_line_bytes;

            new_total_bytes = new_line_bytes * new_height;

            // 分配内存

            new_pixels = (GLubyte*)malloc(new_total_bytes);

            if (new_pixels == 0)

            {

                free(pixels);

                fclose(pFile);

                return 0;

            }

            // 进行像素缩放

            gluScaleImage(GL_RGB,

                width, height, GL_UNSIGNED_BYTE, pixels,

                new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);

            // 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height

            free(pixels);

            pixels = new_pixels;

            width = new_width;

            height = new_height;

        }

    }

    // 分配一个新的纹理编号

    glGenTextures(1, &texture_ID);

    if (texture_ID == 0)

    {

        free(pixels);

        fclose(pFile);

        return 0;

    }

    // 绑定新的纹理,载入纹理并设置纹理参数

    // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复

    glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture_ID);

    glBindTexture(GL_TEXTURE_2D, texture_ID);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,

        GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);

    glBindTexture(GL_TEXTURE_2D, last_texture_ID);

    // 之前为pixels分配的内存可在使用glTexImage2D以后释放

    // 因为此时像素数据已经被OpenGL另行保存了一份(可能被保存到专门的图形硬件中)

    free(pixels);

    return texture_ID;

}

/* 将当前纹理BGR格式转换为BGRA格式

 * 纹理中像素的RGB值如果与指定rgb相差不超过absolute,则将Alpha设置为.0,否则设置为.0

 */

void texture_colorkey(GLubyte r, GLubyte g, GLubyte b, GLubyte absolute)

{

    GLint width, height;

    GLubyte* pixels = 0;

    // 获得纹理的大小信息

    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);

    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);

    // 分配空间并获得纹理像素

    pixels = (GLubyte*)malloc(width * height * 4);

    if (pixels == 0)

        return;

    glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);

    // 修改像素中的Alpha值

    // 其中pixels[i*4], pixels[i*4+1], pixels[i*4+2], pixels[i*4+3]

    //   分别表示第i个像素的蓝、绿、红、Alpha四种分量,表示最小,表示最大

    {

        GLint i;

        GLint count = width * height;

        for (i = 0; i < count; ++i)

        {

            if (abs(pixels[i * 4] - b) <= absolute

                && abs(pixels[i * 4 + 1] - g) <= absolute

                && abs(pixels[i * 4 + 2] - r) <= absolute)

                pixels[i * 4 + 3] = 0;

            else

                pixels[i * 4 + 3] = 255;

        }

    }

    // 将修改后的像素重新设置到纹理中,释放内存

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,

        GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);

    free(pixels);

}

void display(void)

{

    static int initialized = 0;

    static GLuint texWindow = 0;

    static GLuint texPicture = 0;

    // 执行初始化操作,包括:读取相片,读取相框,将相框由BGR颜色转换为BGRA,启用二维纹理

    if (!initialized)

    {

        texPicture = load_texture("pic.bmp");

        texWindow = load_texture("window.bmp");

        glBindTexture(GL_TEXTURE_2D, texWindow);

        texture_colorkey(255, 255, 255, 10);

        glEnable(GL_TEXTURE_2D);

        initialized = 1;

    }

    // 清除屏幕

    glClear(GL_COLOR_BUFFER_BIT);

    // 绘制相片,此时不需要进行Alpha测试,所有的像素都进行绘制

    glBindTexture(GL_TEXTURE_2D, texPicture);

    glDisable(GL_ALPHA_TEST);

    glBegin(GL_QUADS);

    glTexCoord2f(0, 0);     glVertex2f(-1.0f, -1.0f);

    glTexCoord2f(0, 1);     glVertex2f(-1.0f, 1.0f);

    glTexCoord2f(1, 1);     glVertex2f(1.0f, 1.0f);

    glTexCoord2f(1, 0);     glVertex2f(1.0f, -1.0f);

    glEnd();

    // 绘制相框,此时进行Alpha测试,只绘制不透明部分的像素

    glBindTexture(GL_TEXTURE_2D, texWindow);

    glEnable(GL_ALPHA_TEST);

    glAlphaFunc(GL_GREATER, 0.5f);

    glBegin(GL_QUADS);

    glTexCoord2f(0, 0);     glVertex2f(-1.0f, -1.0f);

    glTexCoord2f(0, 1);     glVertex2f(-1.0f, 1.0f);

    glTexCoord2f(1, 1);     glVertex2f(1.0f, 1.0f);

    glTexCoord2f(1, 0);     glVertex2f(1.0f, -1.0f);

    glEnd();

    // 交换缓冲

    glutSwapBuffers();

}

int main(int argc, char* argv[])

{

    // GLUT初始化

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);

    glutInitWindowPosition(100, 100);

    glutInitWindowSize(WindowWidth, WindowHeight);

    glutCreateWindow(WindowTitle);

    glutDisplayFunc(&display);

    glutMainLoop();

    return 0;

}

=========================================================================

参考 :第11讲OpenGL程序出错,希望有大神帮忙解决一下!(有源程序)-CSDN论坛

图片资源:https://download.csdn.net/download/weixin_45322373/75434457

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值