OpenGL 蓝书学习(第四版)第八章

OpenGL 蓝书学习(第四版)第八章

欢迎来到第八章纹理的学习

金字塔(四棱锥)

#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif

#include <iostream>
#include <fstream>
#include <format>
#include <vector>

// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
static GLfloat xPos = 0.0F; // x位置
static GLfloat yPos = 0.0F; // y位置
static GLfloat zPos = 0.0F; // z位置


// Targa 图像文件头结构体
#pragma pack(push, 1)          // 关闭结构体对齐填充
struct TargaHeader {
    // 图像 ID 区长度,0 = 无
    GLubyte idLength;
    GLubyte colorMapType;  // 0 = 无调色板
    GLubyte dataTypeCode;     // 图像数据类型代码 2 = 未压缩真彩图(非 RLE)
    GLushort colorMapOrigin;  // 调色板起始索引
    GLushort colorMapLength;  // 调色板颜色的数量
    GLubyte colorMapDepth;  // 调色板每条颜色的位数
    GLushort xOrigin;   // 图像在屏幕上的 X 坐标
    GLushort yOrigin;   // 图像在屏幕上的 Y 坐标
    GLushort width;   // 图像宽度
    GLushort height;  // 图像高度
    // 每像素 24 位 = RGB 各 8 位
    GLubyte bitsPerPixel;
    // 0 表示自下而上存储
    GLubyte imageDescriptor;
};
#pragma pack(pop)             // 恢复结构体对齐填充

std::ostream& operator<<(std::ostream& os, const TargaHeader& header) {
    os << "TargaHeader { "
       << "idLength=" << static_cast<int>(header.idLength) << ", "
       << "colorMapType=" << static_cast<int>(header.colorMapType) << ", "
       << "dataTypeCode=" << static_cast<int>(header.dataTypeCode) << ", "
       << "colorMapOrigin=" << header.colorMapOrigin << ", "
       << "colorMapLength=" << header.colorMapLength << ", "
       << "colorMapDepth=" << static_cast<int>(header.colorMapDepth) << ", "
       << "xOrigin=" << header.xOrigin << ", "
       << "yOrigin=" << header.yOrigin << ", "
       << "width=" << header.width << ", "
       << "height=" << header.height << ", "
       << "bitsPerPixel=" << static_cast<int>(header.bitsPerPixel) << ", "
       << "imageDescriptor=" << static_cast<int>(header.imageDescriptor)
       << " }";
    return os;
}

void changeSize(const int w, int h) {
    if (h == 0) h = 1; // Prevent division by zero
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, static_cast<double>(w) / static_cast<double>(h), 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

// 用三角形三个顶点求面法线(右手定则叉乘 + 归一化),并设为当前法线。
// 顶点顺序需与 glVertex3f 一致:CCW 正面时法线指向观察者。
void setFaceNormal(const GLfloat ax[3],
                   const GLfloat bx[3],
                   const GLfloat cx[3]) {
    const GLfloat ux = bx[0] - ax[0], uy = bx[1] - ax[1], uz = bx[2] - ax[2];
    const GLfloat vx = cx[0] - ax[0], vy = cx[1] - ax[1], vz = cx[2] - ax[2];
    GLfloat nx = uy * vz - uz * vy;
    GLfloat ny = uz * vx - ux * vz;
    GLfloat nz = ux * vy - uy * vx;
    const GLfloat len = std::sqrt(nx * nx + ny * ny + nz * nz);
    if (len > 0.0F) {
        nx /= len;
        ny /= len;
        nz /= len;
    }
    glNormal3f(nx, ny, nz);
}

void renderScene() {
    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    
    glPushMatrix();
    
    glTranslatef(0.0f, -0.5f, -3.0f);

    glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);
    glRotatef(zRot, 0.0f, 0.0f, 1.0f);

    glColor3f(1.0f, 1.0f, 1.0f);
    glBegin(GL_TRIANGLES);
        // 底部
        constexpr GLfloat vertices[][3] = {
            { 0.0F, 0.8F, 0.0F },  // 顶    0
            { -0.5F, 0.0F, -0.5F },// 左后. 1
            { 0.5F, 0.0F, -0.5F }, // 右后. 2
            { 0.5F, 0.0F, 0.5F },  // 右前 3
            { -0.5F, 0.0F, 0.5F }, // 左前 4
        };
        // 底部
        glNormal3f(0.0F, -1.0F, 0.0F);
        glTexCoord2f(1.0F, 1.0F);
        glVertex3fv(vertices[2]);
        glTexCoord2f(0.0F, 0.0F);
        glVertex3fv(vertices[4]);
        glTexCoord2f(0.0F, 1.0F);
        glVertex3fv(vertices[1]);
        glTexCoord2f(1.0F, 1.0F);
        glVertex3fv(vertices[2]);
        glTexCoord2f(1.0F, 0.0F);
        glVertex3fv(vertices[3]);
        glTexCoord2f(0.0F, 0.0F);
        glVertex3fv(vertices[4]);

        // 正面
        setFaceNormal(vertices[0], vertices[4], vertices[3]);
        glTexCoord2f(0.5F, 1.0F);
        glVertex3fv(vertices[0]);
        glTexCoord2f(0.0F, 0.0F);
        glVertex3fv(vertices[4]);
        glTexCoord2f(1.0F, 0.0F);
        glVertex3fv(vertices[3]);

        // 左面
        setFaceNormal(vertices[0], vertices[1], vertices[4]);
        glTexCoord2f(0.5F, 1.0F);
        glVertex3fv(vertices[0]);
        glTexCoord2f(0.0F, 0.0F);
        glVertex3fv(vertices[1]);
        glTexCoord2f(1.0F, 0.0F);
        glVertex3fv(vertices[4]);

        // 右面
        setFaceNormal(vertices[0], vertices[3], vertices[2]);
        glTexCoord2f(0.5F, 1.0F);
        glVertex3fv(vertices[0]);
        glTexCoord2f(0.0F, 0.0F);
        glVertex3fv(vertices[3]);
        glTexCoord2f(1.0F, 0.0F);
        glVertex3fv(vertices[2]);

        // 背面
        setFaceNormal(vertices[0], vertices[2], vertices[1]);
        glTexCoord2f(0.5F, 1.0F);
        glVertex3fv(vertices[0]);
        glTexCoord2f(0.0F, 0.0F);
        glVertex3fv(vertices[2]);
        glTexCoord2f(1.0F, 0.0F);
        glVertex3fv(vertices[1]);

    glEnd();
    
    glPopMatrix();

    glutSwapBuffers();
}

void setupRc() {

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    glEnable(GL_DEPTH_TEST);	// Hidden surface removal
    glFrontFace(GL_CCW);		// Counter clock-wise polygons face out
    glEnable(GL_CULL_FACE);		// Do not calculate inside of jet

    // 灯光
    GLfloat  whiteLight[] = { 0.05f, 0.05f, 0.05f, 1.0f };
    GLfloat  sourceLight[] = { 0.25f, 0.25f, 0.25f, 1.0f };
    GLfloat	 lightPos[] = { -10.f, 5.0f, 5.0f, 1.0f };

    // Enable lighting
    glEnable(GL_LIGHTING);

    // Setup and enable light 0
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, whiteLight);
    glLightfv(GL_LIGHT0, GL_AMBIENT, sourceLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, sourceLight);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
    glEnable(GL_LIGHT0);

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

    // 加载纹理
    std::ifstream file("../stone.tga", std::ios::binary);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open stone.tga");
    }
    TargaHeader header;
    file.read(reinterpret_cast<char*>(&header), sizeof(TargaHeader));
    if(file.fail()) {
        throw std::runtime_error("Failed to read Targa header from stone.tga");
    }
    std::cout << "Targa header: " << header << std::endl;
    const std::size_t imageSize = header.width * header.height * (header.bitsPerPixel / 8); 
    std::vector<char> imageData(imageSize);
    file.read(imageData.data(), imageSize);
    if(file.fail()) {
        throw std::runtime_error("Failed to read Targa image data from stone.tga");
    }
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, header.width, header.height, 0, GL_BGR, GL_UNSIGNED_BYTE, imageData.data());
    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_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glEnable(GL_TEXTURE_2D);
}

// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
    std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case 27:    // ESC 键
        case 'q':
        case 'Q':
            std::cout << "keyboard close..." << "\n";
            std::exit(0);
            break;
        case 'a':
        case 'A':
            zRot -= 5.0F; // 绕 Z 轴逆时针旋转
            glutPostRedisplay();
            break;
        case 'd':
        case 'D':
            zRot += 5.0F; // 绕 Z 轴顺时针旋转
            glutPostRedisplay();
            break;
        case 'w':
        case 'W':
            yPos += 0.1F; // 向上移动
            glutPostRedisplay();
            break;
        case 's':
        case 'S':
            yPos -= 0.1F; // 向下移动
            glutPostRedisplay();
            break;
        case 'z':
        case 'Z':
            xPos += 0.1F; // 向前移动
            glutPostRedisplay();
            break;
        case 'x':
        case 'X':
            xPos -= 0.1F; // 向后移动
            glutPostRedisplay();
            break;
        case 'c':
        case 'C':
            zPos += 0.1F; // 向前移动
            glutPostRedisplay();
            break;
        case 'v':
        case 'V':
            zPos -= 0.1F; // 向后移动
            glutPostRedisplay();
            break;
        default:
            break;
    }
}
 
void specialKeys(const int key, const int x, const int y) {
    std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case GLUT_KEY_UP:
            xRot -= 5.0F;
            break;
        case GLUT_KEY_DOWN:
            xRot += 5.0F;
            break;
        case GLUT_KEY_LEFT:
            yRot -= 5.0F;
            break;
        case GLUT_KEY_RIGHT:
            yRot += 5.0F;
            break;
        default:
            break;
    }
    // Refresh the Window
    glutPostRedisplay();
}

auto main(const int argc, const char** argv) -> int {
    std::cout << "Hello, World!" << std::endl;
    glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    const int mainWindowId = glutCreateWindow("OpenGL");
    if (!mainWindowId) {
        throw std::runtime_error("Failed to create OpenGL window");
    }
    glutDisplayFunc(renderScene);
    glutReshapeFunc(changeSize);
    glutSpecialFunc(specialKeys);   // 特殊键事件
    glutKeyboardFunc(keyboard);  // 普通键事件
    setupRc();
    glutMainLoop();
    return 0;
}

卡通纹理

#include <iosfwd>
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif

#include <iostream>
#include <format>
#include <numbers>

// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLfloat vLightDir[3] = { -1.0F, 1.0F, 1.0F }; // 眼睛坐标

// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
static GLfloat xPos = 0.0F; // x位置
static GLfloat yPos = 0.0F; // y位置
static GLfloat zPos = 0.0F; // z位置


void changeSize(const int w, int h) {
    if (h == 0) h = 1; // Prevent division by zero
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, static_cast<double>(w) / static_cast<double>(h), 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void normalize(GLfloat v[3]) {
    const GLfloat len = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    if (len > 0.0F) {
        v[0] /= len;
        v[1] /= len;
        v[2] /= len;
    }
}

GLfloat toonShade(const GLfloat n[3], const GLfloat m[16], const GLfloat light[3]) {
    GLfloat e[3] = {
        m[0] * n[0] + m[4] * n[1] + m[8]  * n[2],
        m[1] * n[0] + m[5] * n[1] + m[9]  * n[2],
        m[2] * n[0] + m[6] * n[1] + m[10] * n[2],
    };
    normalize(e);
    return light[0] * e[0] + light[1] * e[1] + light[2] * e[2];
}

void drawTorus(const GLfloat majorRadius, const GLfloat minorRadius, const int numMajor, const int numMinor) {
    
    GLfloat modelViewMatrix[16];
    // 获取当前模型视图矩阵
    glGetFloatv(GL_MODELVIEW_MATRIX, modelViewMatrix);
    
    GLfloat lightDir[3] = { vLightDir[0], vLightDir[1], vLightDir[2] };
    normalize(lightDir);

    const GLfloat majorStep = 2.0F * PI / static_cast<GLfloat>(numMajor);
    const GLfloat minorStep = 2.0F * PI / static_cast<GLfloat>(numMinor);
    for (int i = 0; i < numMajor; ++i) {
        const GLfloat u0 = i * majorStep;   
        const GLfloat u1 = u0 + majorStep;
        glBegin(GL_TRIANGLE_STRIP);
        for (int j = 0; j <= numMinor; ++j) {
            const GLfloat v0 = j * minorStep;

            // 计算两个顶点的坐标
            const GLfloat n0[3] = {
                std::cos(v0) * std::cos(u0),
                std::cos(v0) * std::sin(u0),
                std::sin(v0)
            };
            const GLfloat x00 = (majorRadius + minorRadius * std::cos(v0)) * std::cos(u0);
            const GLfloat y00 = (majorRadius + minorRadius * std::cos(v0)) * std::sin(u0);
            const GLfloat z00 = minorRadius * std::sin(v0);

            const GLfloat n1[3] = {
                std::cos(v0) * std::cos(u1),
                std::cos(v0) * std::sin(u1),
                std::sin(v0)
            };
            const GLfloat x01 = (majorRadius + minorRadius * std::cos(v0)) * std::cos(u1);
            const GLfloat y01 = (majorRadius + minorRadius * std::cos(v0)) * std::sin(u1);
            const GLfloat z01 = minorRadius * std::sin(v0);

            glTexCoord1f(toonShade(n0, modelViewMatrix, lightDir));    // s = N·L
            glVertex3f(x00, y00, z00);
            glTexCoord1f(toonShade(n1, modelViewMatrix, lightDir));    // s = N·L
            glVertex3f(x01, y01, z01);
        }
        glEnd();
    }
}

void renderScene() {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glPushMatrix();    

    glTranslatef(0.0F, 0.0F, -4.0F);
    glRotatef(xRot, 1.0F, 0.0F, 0.0F);
    glRotatef(yRot, 0.0F, 1.0F, 0.0F);
    glRotatef(zRot, 0.0F, 0.0F, 1.0F);
    glColor3f(1.0f, 1.0f, 1.0f);
    
    drawTorus(1.0F, 0.3F, 32, 16);
    
    glPopMatrix();

    glutSwapBuffers();
}

void setupRc() {

    glClearColor(0.0f, 0.0f, 0.5f, 1.0f);
    glEnable(GL_DEPTH_TEST);	// Hidden surface removal
    glFrontFace(GL_CCW);		// Counter clock-wise polygons face out
    glEnable(GL_CULL_FACE);		// Do not calculate inside of jet

    // 加载1d纹理
    constexpr GLubyte toonTable[][3] = {
        {0, 32, 0},
        {0, 64, 0},
        {0, 128, 0},
        {0, 192, 0},
    };
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    // s = N·L   (法线与光线方向的点积,范围 -1 … 1) s 超出 [0,1] 钳到边缘
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, toonTable);
    glEnable(GL_TEXTURE_1D);
}

// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
    std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case 27:    // ESC 键
        case 'q':
        case 'Q':
            std::cout << "keyboard close..." << "\n";
            std::exit(0);
            break;
        case 'a':
        case 'A':
            zRot -= 5.0F; // 绕 Z 轴逆时针旋转
            glutPostRedisplay();
            break;
        case 'd':
        case 'D':
            zRot += 5.0F; // 绕 Z 轴顺时针旋转
            glutPostRedisplay();
            break;
        case 'w':
        case 'W':
            yPos += 0.1F; // 向上移动
            glutPostRedisplay();
            break;
        case 's':
        case 'S':
            yPos -= 0.1F; // 向下移动
            glutPostRedisplay();
            break;
        case 'z':
        case 'Z':
            xPos += 0.1F; // 向前移动
            glutPostRedisplay();
            break;
        case 'x':
        case 'X':
            xPos -= 0.1F; // 向后移动
            glutPostRedisplay();
            break;
        case 'c':
        case 'C':
            zPos += 0.1F; // 向前移动
            glutPostRedisplay();
            break;
        case 'v':
        case 'V':
            zPos -= 0.1F; // 向后移动
            glutPostRedisplay();
            break;
        default:
            break;
    }
}
 
void specialKeys(const int key, const int x, const int y) {
    std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case GLUT_KEY_UP:
            xRot -= 5.0F;
            break;
        case GLUT_KEY_DOWN:
            xRot += 5.0F;
            break;
        case GLUT_KEY_LEFT:
            yRot -= 5.0F;
            break;
        case GLUT_KEY_RIGHT:
            yRot += 5.0F;
            break;
        default:
            break;
    }
    // Refresh the Window
    glutPostRedisplay();
}

auto main(const int argc, const char** argv) -> int {
    std::cout << "Hello, World!" << std::endl;
    glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    const int mainWindowId = glutCreateWindow("OpenGL");
    if (!mainWindowId) {
        throw std::runtime_error("Failed to create OpenGL window");
    }
    glutDisplayFunc(renderScene);
    glutReshapeFunc(changeSize);
    glutSpecialFunc(specialKeys);   // 特殊键事件
    glutKeyboardFunc(keyboard);  // 普通键事件
    setupRc();
    glutMainLoop();
    return 0;
}

mip过滤

#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <array>
#include <iosfwd>
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif

#include <iostream>
#include <format>
#include <numbers>
#include <fstream>
#include <vector>

// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLuint NUM_TEXTURES = 3;
static constexpr std::array<std::string, NUM_TEXTURES> textureFiles = {
    "../brick.tga", // 0 砖块
    "../floor.tga", // 1 地板
    "../ceiling.tga" // 2 天花板
};

// 变量
static GLfloat zPos = -60.0F; // z位置
static int mainWindowId; // 主窗口 ID
static GLuint textureIds[NUM_TEXTURES];


// Targa 图像文件头结构体
#pragma pack(push, 1)          // 关闭结构体对齐填充
struct TargaHeader {
    // 图像 ID 区长度,0 = 无
    GLubyte idLength;
    GLubyte colorMapType;  // 0 = 无调色板
    GLubyte dataTypeCode;     // 图像数据类型代码 2 = 未压缩真彩图(非 RLE)
    GLushort colorMapOrigin;  // 调色板起始索引
    GLushort colorMapLength;  // 调色板颜色的数量
    GLubyte colorMapDepth;  // 调色板每条颜色的位数
    GLushort xOrigin;   // 图像在屏幕上的 X 坐标
    GLushort yOrigin;   // 图像在屏幕上的 Y 坐标
    GLushort width;   // 图像宽度
    GLushort height;  // 图像高度
    // 每像素 24 位 = RGB 各 8 位
    GLubyte bitsPerPixel;
    // 0 表示自下而上存储
    GLubyte imageDescriptor;
};
#pragma pack(pop)             // 恢复结构体对齐填充
 
std::ostream& operator<<(std::ostream& os, const TargaHeader& header) {
    os << "TargaHeader { "
       << "idLength=" << static_cast<int>(header.idLength) << ", "
       << "colorMapType=" << static_cast<int>(header.colorMapType) << ", "
       << "dataTypeCode=" << static_cast<int>(header.dataTypeCode) << ", "
       << "colorMapOrigin=" << header.colorMapOrigin << ", "
       << "colorMapLength=" << header.colorMapLength << ", "
       << "colorMapDepth=" << static_cast<int>(header.colorMapDepth) << ", "
       << "xOrigin=" << header.xOrigin << ", "
       << "yOrigin=" << header.yOrigin << ", "
       << "width=" << header.width << ", "
       << "height=" << header.height << ", "
       << "bitsPerPixel=" << static_cast<int>(header.bitsPerPixel) << ", "
       << "imageDescriptor=" << static_cast<int>(header.imageDescriptor)
       << " }";
    return os;
}

void changeSize(const int w, int h) {
    if (h == 0) h = 1; // Prevent division by zero
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, static_cast<double>(w) / static_cast<double>(h), 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void normalize(GLfloat v[3]) {
    const GLfloat len = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    if (len > 0.0F) {
        v[0] /= len;
        v[1] /= len;
        v[2] /= len;
    }
}

GLfloat toonShade(const GLfloat n[3], const GLfloat m[16], const GLfloat light[3]) {
    GLfloat e[3] = {
        m[0] * n[0] + m[4] * n[1] + m[8]  * n[2],
        m[1] * n[0] + m[5] * n[1] + m[9]  * n[2],
        m[2] * n[0] + m[6] * n[1] + m[10] * n[2],
    };
    normalize(e);
    return light[0] * e[0] + light[1] * e[1] + light[2] * e[2];
}

void renderScene() {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glPushMatrix();    
        
    glTranslatef(0.0F, 0.0F, zPos);

    for(GLfloat z = 60.0F; z >= 0.0F; z -= 10.0F){
        // 地板
        glBindTexture(GL_TEXTURE_2D, textureIds[1]); // 绑定第一个纹理
        glBegin(GL_QUADS);
            glTexCoord2f(0.0F, 0.0F); 
            glVertex3f(-10.0F, -10.0F, z);
            glTexCoord2f(1.0F, 0.0F); 
            glVertex3f( 10.0F, -10.0F, z);
            glTexCoord2f(1.0F, 1.0F); 
            glVertex3f( 10.0F,  -10.0F, z - 10.0F);
            glTexCoord2f(0.0F, 1.0F); 
            glVertex3f(-10.0F,  -10.0F, z - 10.0F);
        glEnd();
        // 天花板
        glBindTexture(GL_TEXTURE_2D, textureIds[2]); // 绑定天花板纹理
        glBegin(GL_QUADS);
            glTexCoord2f(0.0F, 1.0F); 
            glVertex3f(-10.0F,  10.0F, z - 10.0F);
            glTexCoord2f(1.0F, 1.0F); 
            glVertex3f( 10.0F,  10.0F, z - 10.0F);
            glTexCoord2f(1.0F, 0.0F); 
            glVertex3f( 10.0F,  10.0F, z);
            glTexCoord2f(0.0F, 0.0F); 
            glVertex3f(-10.0F,  10.0F, z);
        glEnd();
        // 左
        glBindTexture(GL_TEXTURE_2D, textureIds[0]); // 绑定左墙纹理
        glBegin(GL_QUADS);
            glTexCoord2f(0.0F, 0.0F); 
            glVertex3f(-10.0F, -10.0F, z);
            glTexCoord2f(1.0F, 0.0F); 
            glVertex3f(-10.0F, -10.0F, z - 10.0F);
            glTexCoord2f(1.0F, 1.0F); 
            glVertex3f(-10.0F,  10.0F, z - 10.0F);
            glTexCoord2f(0.0F, 1.0F); 
            glVertex3f(-10.0F,  10.0F, z);
        glEnd();

        // 右
        glBindTexture(GL_TEXTURE_2D, textureIds[0]); // 绑定右墙纹理
        glBegin(GL_QUADS);
            glTexCoord2f(0.0F, 1.0F); 
            glVertex3f(10.0F, 10.0F, z);
            glTexCoord2f(1.0F, 1.0F); 
            glVertex3f(10.0F, 10.0F, z - 10.0F);
            glTexCoord2f(1.0F, 0.0F); 
            glVertex3f(10.0F,  -10.0F, z - 10.0F);
            glTexCoord2f(0.0F, 0.0F); 
            glVertex3f(10.0F,  -10.0F, z);
        glEnd();
    }
    
    
    glPopMatrix();

    glutSwapBuffers();
}

void setupRc() {

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    // 开启2d纹理
    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

    glGenTextures(NUM_TEXTURES, textureIds);
    for (GLuint i = 0; i < NUM_TEXTURES; ++i) {
        glBindTexture(GL_TEXTURE_2D, textureIds[i]);

        // 加载纹理数据
        std::ifstream file(textureFiles[i], std::ios::binary);
        if (!file.is_open()) {
            throw std::runtime_error("Failed to open " + textureFiles[i]);
        }
        TargaHeader header;
        file.read(reinterpret_cast<char*>(&header), sizeof(TargaHeader));
        if(file.fail()) {
            throw std::runtime_error("Failed to read Targa header from " + textureFiles[i]);
        }
        std::cout << "Targa header: " << header << std::endl;
        const std::size_t imageSize = header.width * header.height * (header.bitsPerPixel / 8); 
        std::vector<char> imageData(imageSize);
        file.read(imageData.data(), imageSize);
        if(file.fail()) {
            throw std::runtime_error("Failed to read Targa image data from " + textureFiles[i]);
        }
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, header.width, header.height, GL_BGR, GL_UNSIGNED_BYTE, imageData.data());

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    }
}
void shutdownRc() {
    glDeleteTextures(NUM_TEXTURES, textureIds);
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
    std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case 27:    // ESC 键
        case 'q':
        case 'Q':
            std::cout << "keyboard close..." << "\n";
            std::exit(0);
            break;
        case 'a':
        case 'A':
            break;
        case 'd':
        case 'D':
            // glutPostRedisplay();
            break;
        case 'w':
        case 'W':
            break;
        case 's':
        case 'S':
            break;
        case 'z':
        case 'Z':
            break;
        case 'x':
        case 'X':
            break;
        case 'c':
        case 'C':
            break;
        case 'v':
        case 'V':
            break;
        default:
            break;
    }
}
 
void specialKeys(const int key, const int x, const int y) {
    std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case GLUT_KEY_UP:
            zPos += 1.0f;
            break;
        case GLUT_KEY_DOWN:
            zPos -= 1.0f;
            break;
        case GLUT_KEY_LEFT:
            break;
        case GLUT_KEY_RIGHT:
            break;
        default:
            break;
    }
    // Refresh the Window
    glutPostRedisplay();
}

void processMenu(const int value) {
    std::cout << std::format("Menu selected: {}", value) << "\n";
    for(GLuint i = 0; i < NUM_TEXTURES; ++i) {
        glBindTexture(GL_TEXTURE_2D, textureIds[i]);
        switch (value) {
            case 0:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                break;
            case 1:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                break;
            case 2:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
                break;
            case 3:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
                break;
            case 4:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
                break;
            case 5:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
                break;
        }
    }
    glutSetWindow(mainWindowId);
    glutPostRedisplay();
}

auto main(const int argc, const char** argv) -> int {
    std::cout << "Hello, World!" << std::endl;
    glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    mainWindowId = glutCreateWindow("OpenGL");
    if (!mainWindowId) {
        throw std::runtime_error("Failed to create OpenGL window");
    }
    glutDisplayFunc(renderScene);
    glutReshapeFunc(changeSize);
    glutSpecialFunc(specialKeys);   // 特殊键事件
    glutKeyboardFunc(keyboard);  // 普通键事件

    glutCreateMenu(processMenu);
    glutAddMenuEntry("GL_NEAREST",0);
    glutAddMenuEntry("GL_LINEAR",1);
    glutAddMenuEntry("GL_NEAREST_MIPMAP_NEAREST",2);
    glutAddMenuEntry("GL_NEAREST_MIPMAP_LINEAR", 3);
    glutAddMenuEntry("GL_LINEAR_MIPMAP_NEAREST", 4);
    glutAddMenuEntry("GL_LINEAR_MIPMAP_LINEAR", 5);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    setupRc();
    glutMainLoop();
    return 0;
}

对球进行纹理贴图

#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <array>
#include <iosfwd>
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif

#include <iostream>
#include <format>
#include <numbers>
#include <fstream>
#include <vector>

// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLuint NUM_TEXTURES = 3;
static constexpr std::array<std::string, NUM_TEXTURES> textureFiles = {
    "../brick.tga", // 0 砖块
    "../floor.tga", // 1 地板
    "../ceiling.tga" // 2 天花板
};

// 变量
static GLfloat xRot = 0.0F; // z位置
static GLfloat yRot = 0.0F; // z位置
static GLfloat zRot = 0.0F; // z位置
static GLfloat zPos = -4.0F; // z位置
static int mainWindowId; // 主窗口 ID
static GLuint textureIds[NUM_TEXTURES];


// Targa 图像文件头结构体
#pragma pack(push, 1)          // 关闭结构体对齐填充
struct TargaHeader {
    // 图像 ID 区长度,0 = 无
    GLubyte idLength;
    GLubyte colorMapType;  // 0 = 无调色板
    GLubyte dataTypeCode;     // 图像数据类型代码 2 = 未压缩真彩图(非 RLE)
    GLushort colorMapOrigin;  // 调色板起始索引
    GLushort colorMapLength;  // 调色板颜色的数量
    GLubyte colorMapDepth;  // 调色板每条颜色的位数
    GLushort xOrigin;   // 图像在屏幕上的 X 坐标
    GLushort yOrigin;   // 图像在屏幕上的 Y 坐标
    GLushort width;   // 图像宽度
    GLushort height;  // 图像高度
    // 每像素 24 位 = RGB 各 8 位
    GLubyte bitsPerPixel;
    // 0 表示自下而上存储
    GLubyte imageDescriptor;
};
#pragma pack(pop)             // 恢复结构体对齐填充
 
std::ostream& operator<<(std::ostream& os, const TargaHeader& header) {
    os << "TargaHeader { "
       << "idLength=" << static_cast<int>(header.idLength) << ", "
       << "colorMapType=" << static_cast<int>(header.colorMapType) << ", "
       << "dataTypeCode=" << static_cast<int>(header.dataTypeCode) << ", "
       << "colorMapOrigin=" << header.colorMapOrigin << ", "
       << "colorMapLength=" << header.colorMapLength << ", "
       << "colorMapDepth=" << static_cast<int>(header.colorMapDepth) << ", "
       << "xOrigin=" << header.xOrigin << ", "
       << "yOrigin=" << header.yOrigin << ", "
       << "width=" << header.width << ", "
       << "height=" << header.height << ", "
       << "bitsPerPixel=" << static_cast<int>(header.bitsPerPixel) << ", "
       << "imageDescriptor=" << static_cast<int>(header.imageDescriptor)
       << " }";
    return os;
}

void changeSize(const int w, int h) {
    if (h == 0) h = 1; // Prevent division by zero
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, static_cast<double>(w) / static_cast<double>(h), 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void normalize(GLfloat v[3]) {
    const GLfloat len = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    if (len > 0.0F) {
        v[0] /= len;
        v[1] /= len;
        v[2] /= len;
    }
}

GLfloat toonShade(const GLfloat n[3], const GLfloat m[16], const GLfloat light[3]) {
    GLfloat e[3] = {
        m[0] * n[0] + m[4] * n[1] + m[8]  * n[2],
        m[1] * n[0] + m[5] * n[1] + m[9]  * n[2],
        m[2] * n[0] + m[6] * n[1] + m[10] * n[2],
    };
    normalize(e);
    return light[0] * e[0] + light[1] * e[1] + light[2] * e[2];
}
void drawSphere(const GLfloat radius, const GLint slices, const GLint stacks) {
    const GLfloat sliceStep = 2.0F * PI / static_cast<GLfloat>(slices);
    const GLfloat stackStep = PI / static_cast<GLfloat>(stacks);
    for (GLint i = 0; i <= stacks; ++i) {
        const GLfloat stackAngle1 = i * stackStep;
        const GLfloat stackAngle2 = (i + 1) * stackStep;
        glBegin(GL_TRIANGLE_STRIP);
        for (GLint j = 0; j <= slices; ++j) {
            const GLfloat sliceAngle1 = j * sliceStep;
            const GLfloat sliceAngle2 = (j + 1) * sliceStep;

            const GLfloat nx1 = std::sin(stackAngle1) * std::cos(sliceAngle1);
            const GLfloat ny1 = std::cos(stackAngle1);
            const GLfloat nz1 = std::sin(stackAngle1) * std::sin(sliceAngle1);
            const GLfloat x1 = radius * nx1;
            const GLfloat y1 = radius * ny1;
            const GLfloat z1 = radius * nz1;

            const GLfloat nx2 = std::sin(stackAngle2) * std::cos(sliceAngle1);
            const GLfloat ny2 = std::cos(stackAngle2);
            const GLfloat nz2 = std::sin(stackAngle2) * std::sin(sliceAngle1);
            const GLfloat x2 = radius * nx2;
            const GLfloat y2 = radius * ny2;
            const GLfloat z2 = radius * nz2;

            glNormal3f(nx1, ny1, nz1);
            glTexCoord2f(static_cast<GLfloat>(j) / slices, // 左下角
                         1.0F - static_cast<GLfloat>(i) / stacks);
            glVertex3f(x1, y1, z1);

            glNormal3f(nx2, ny2, nz2);
            glTexCoord2f(static_cast<GLfloat>(j) / slices,
            1.0F - static_cast<GLfloat>(i + 1) / stacks);
            glVertex3f(x2, y2, z2);
        }
        glEnd();
    }
}
void renderScene() {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glPushMatrix();    
        
    glTranslatef(0.0F, 0.0F, zPos);
    glRotatef(xRot, 1.0F, 0.0F, 0.0F);
    glRotatef(yRot, 0.0F, 1.0F, 0.0F);
    glRotatef(zRot, 0.0F, 0.0F, 1.0F);

    glBindTexture(GL_TEXTURE_2D, textureIds[0]);
    drawSphere(1.0F, 36, 18);
    
    glPopMatrix();

    glutSwapBuffers();
}

void setupRc() {

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);

    // 开启2d纹理
    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

    glGenTextures(NUM_TEXTURES, textureIds);
    for (GLuint i = 0; i < NUM_TEXTURES; ++i) {
        glBindTexture(GL_TEXTURE_2D, textureIds[i]);

        // 加载纹理数据
        std::ifstream file(textureFiles[i], std::ios::binary);
        if (!file.is_open()) {
            throw std::runtime_error("Failed to open " + textureFiles[i]);
        }
        TargaHeader header;
        file.read(reinterpret_cast<char*>(&header), sizeof(TargaHeader));
        if(file.fail()) {
            throw std::runtime_error("Failed to read Targa header from " + textureFiles[i]);
        }
        std::cout << "Targa header: " << header << std::endl;
        const std::size_t imageSize = header.width * header.height * (header.bitsPerPixel / 8); 
        std::vector<char> imageData(imageSize);
        file.read(imageData.data(), imageSize);
        if(file.fail()) {
            throw std::runtime_error("Failed to read Targa image data from " + textureFiles[i]);
        }
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, header.width, header.height, GL_BGR, GL_UNSIGNED_BYTE, imageData.data());

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    }
}
void shutdownRc() {
    glDeleteTextures(NUM_TEXTURES, textureIds);
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
    std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case 27:    // ESC 键
        case 'q':
        case 'Q':
            std::cout << "keyboard close..." << "\n";
            std::exit(0);
            break;
        case 'a':
        case 'A':
            yRot -= 5.0F;
            break;
        case 'd':
        case 'D':
            yRot += 5.0F;
            // glutPostRedisplay();
            break;
        case 'w':
        case 'W':
            xRot -= 5.0F;
            break;
        case 's':
        case 'S':
            xRot += 5.0F;
            break;
        case 'z':
        case 'Z':
            zRot -= 5.0F;
            break;
        case 'x':
        case 'X':
            zRot += 5.0F;
            break;
        case 'c':
        case 'C':
            break;
        case 'v':
        case 'V':
            break;
        default:
            break;
    }
    glutPostRedisplay();
}
 
void specialKeys(const int key, const int x, const int y) {
    std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case GLUT_KEY_UP:
            zPos += 1.0f;
            break;
        case GLUT_KEY_DOWN:
            zPos -= 1.0f;
            break;
        case GLUT_KEY_LEFT:
            break;
        case GLUT_KEY_RIGHT:
            break;
        default:
            break;
    }
    // Refresh the Window
    glutPostRedisplay();
}

void processMenu(const int value) {
    std::cout << std::format("Menu selected: {}", value) << "\n";
    for(GLuint i = 0; i < NUM_TEXTURES; ++i) {
        glBindTexture(GL_TEXTURE_2D, textureIds[i]);
        switch (value) {
            case 0:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                break;
            case 1:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                break;
            case 2:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
                break;
            case 3:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
                break;
            case 4:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
                break;
            case 5:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
                break;
        }
    }
    glutSetWindow(mainWindowId);
    glutPostRedisplay();
}

auto main(const int argc, const char** argv) -> int {
    std::cout << "Hello, World!" << std::endl;
    glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    mainWindowId = glutCreateWindow("OpenGL");
    if (!mainWindowId) {
        throw std::runtime_error("Failed to create OpenGL window");
    }
    glutDisplayFunc(renderScene);
    glutReshapeFunc(changeSize);
    glutSpecialFunc(specialKeys);   // 特殊键事件
    glutKeyboardFunc(keyboard);  // 普通键事件

    glutCreateMenu(processMenu);
    glutAddMenuEntry("GL_NEAREST",0);
    glutAddMenuEntry("GL_LINEAR",1);
    glutAddMenuEntry("GL_NEAREST_MIPMAP_NEAREST",2);
    glutAddMenuEntry("GL_NEAREST_MIPMAP_LINEAR", 3);
    glutAddMenuEntry("GL_LINEAR_MIPMAP_NEAREST", 4);
    glutAddMenuEntry("GL_LINEAR_MIPMAP_LINEAR", 5);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    setupRc();
    glutMainLoop();
    return 0;
}

对圆环进行纹理贴图

#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <array>
#include <iosfwd>
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif

#include <iostream>
#include <format>
#include <numbers>
#include <fstream>
#include <vector>

// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLuint NUM_TEXTURES = 3;
static constexpr std::array<std::string, NUM_TEXTURES> textureFiles = {
    "../brick.tga", // 0 砖块
    "../floor.tga", // 1 地板
    "../ceiling.tga" // 2 天花板
};

// 变量
static GLfloat xRot = 0.0F; // z位置
static GLfloat yRot = 0.0F; // z位置
static GLfloat zRot = 0.0F; // z位置
static GLfloat zPos = -4.0F; // z位置
static int mainWindowId; // 主窗口 ID
static GLuint textureIds[NUM_TEXTURES];


// Targa 图像文件头结构体
#pragma pack(push, 1)          // 关闭结构体对齐填充
struct TargaHeader {
    // 图像 ID 区长度,0 = 无
    GLubyte idLength;
    GLubyte colorMapType;  // 0 = 无调色板
    GLubyte dataTypeCode;     // 图像数据类型代码 2 = 未压缩真彩图(非 RLE)
    GLushort colorMapOrigin;  // 调色板起始索引
    GLushort colorMapLength;  // 调色板颜色的数量
    GLubyte colorMapDepth;  // 调色板每条颜色的位数
    GLushort xOrigin;   // 图像在屏幕上的 X 坐标
    GLushort yOrigin;   // 图像在屏幕上的 Y 坐标
    GLushort width;   // 图像宽度
    GLushort height;  // 图像高度
    // 每像素 24 位 = RGB 各 8 位
    GLubyte bitsPerPixel;
    // 0 表示自下而上存储
    GLubyte imageDescriptor;
};
#pragma pack(pop)             // 恢复结构体对齐填充
 
std::ostream& operator<<(std::ostream& os, const TargaHeader& header) {
    os << "TargaHeader { "
       << "idLength=" << static_cast<int>(header.idLength) << ", "
       << "colorMapType=" << static_cast<int>(header.colorMapType) << ", "
       << "dataTypeCode=" << static_cast<int>(header.dataTypeCode) << ", "
       << "colorMapOrigin=" << header.colorMapOrigin << ", "
       << "colorMapLength=" << header.colorMapLength << ", "
       << "colorMapDepth=" << static_cast<int>(header.colorMapDepth) << ", "
       << "xOrigin=" << header.xOrigin << ", "
       << "yOrigin=" << header.yOrigin << ", "
       << "width=" << header.width << ", "
       << "height=" << header.height << ", "
       << "bitsPerPixel=" << static_cast<int>(header.bitsPerPixel) << ", "
       << "imageDescriptor=" << static_cast<int>(header.imageDescriptor)
       << " }";
    return os;
}

void changeSize(const int w, int h) {
    if (h == 0) h = 1; // Prevent division by zero
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, static_cast<double>(w) / static_cast<double>(h), 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void normalize(GLfloat v[3]) {
    const GLfloat len = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    if (len > 0.0F) {
        v[0] /= len;
        v[1] /= len;
        v[2] /= len;
    }
}

GLfloat toonShade(const GLfloat n[3], const GLfloat m[16], const GLfloat light[3]) {
    GLfloat e[3] = {
        m[0] * n[0] + m[4] * n[1] + m[8]  * n[2],
        m[1] * n[0] + m[5] * n[1] + m[9]  * n[2],
        m[2] * n[0] + m[6] * n[1] + m[10] * n[2],
    };
    normalize(e);
    return light[0] * e[0] + light[1] * e[1] + light[2] * e[2];
}
void drawSphere(const GLfloat radius, const GLint slices, const GLint stacks) {
    const GLfloat sliceStep = 2.0F * PI / static_cast<GLfloat>(slices);
    const GLfloat stackStep = PI / static_cast<GLfloat>(stacks);
    for (GLint i = 0; i <= stacks; ++i) {
        const GLfloat stackAngle1 = i * stackStep;
        const GLfloat stackAngle2 = (i + 1) * stackStep;
        glBegin(GL_TRIANGLE_STRIP);
        for (GLint j = 0; j <= slices; ++j) {
            const GLfloat sliceAngle1 = j * sliceStep;
            const GLfloat sliceAngle2 = (j + 1) * sliceStep;

            const GLfloat nx1 = std::sin(stackAngle1) * std::cos(sliceAngle1);
            const GLfloat ny1 = std::cos(stackAngle1);
            const GLfloat nz1 = std::sin(stackAngle1) * std::sin(sliceAngle1);
            const GLfloat x1 = radius * nx1;
            const GLfloat y1 = radius * ny1;
            const GLfloat z1 = radius * nz1;

            const GLfloat nx2 = std::sin(stackAngle2) * std::cos(sliceAngle1);
            const GLfloat ny2 = std::cos(stackAngle2);
            const GLfloat nz2 = std::sin(stackAngle2) * std::sin(sliceAngle1);
            const GLfloat x2 = radius * nx2;
            const GLfloat y2 = radius * ny2;
            const GLfloat z2 = radius * nz2;

            glNormal3f(nx1, ny1, nz1);
            glTexCoord2f(static_cast<GLfloat>(j) / slices, // 左下角
                         1.0F - static_cast<GLfloat>(i) / stacks);
            glVertex3f(x1, y1, z1);

            glNormal3f(nx2, ny2, nz2);
            glTexCoord2f(static_cast<GLfloat>(j) / slices,
            1.0F - static_cast<GLfloat>(i + 1) / stacks);
            glVertex3f(x2, y2, z2);
        }
        glEnd();
    }
}
void drawTorus(const GLfloat majorRadius, const GLfloat minorRadius, const int numMajor, const int numMinor) {
    
    const GLfloat majorStep = 2.0F * PI / static_cast<GLfloat>(numMajor);
    const GLfloat minorStep = 2.0F * PI / static_cast<GLfloat>(numMinor);
    for (int i = 0; i < numMajor; ++i) {
        const GLfloat u0 = i * majorStep;   
        const GLfloat u1 = u0 + majorStep;
        glBegin(GL_TRIANGLE_STRIP);
        for (int j = 0; j <= numMinor; ++j) {
            const GLfloat v0 = j * minorStep;
 
            // 计算两个顶点的坐标
            const GLfloat n0[3] = {
                std::cos(v0) * std::cos(u0),
                std::cos(v0) * std::sin(u0),
                std::sin(v0)
            };
            const GLfloat x00 = (majorRadius + minorRadius * std::cos(v0)) * std::cos(u0);
            const GLfloat y00 = (majorRadius + minorRadius * std::cos(v0)) * std::sin(u0);
            const GLfloat z00 = minorRadius * std::sin(v0);
 
            const GLfloat n1[3] = {
                std::cos(v0) * std::cos(u1),
                std::cos(v0) * std::sin(u1),
                std::sin(v0)
            };
            const GLfloat x01 = (majorRadius + minorRadius * std::cos(v0)) * std::cos(u1);
            const GLfloat y01 = (majorRadius + minorRadius * std::cos(v0)) * std::sin(u1);
            const GLfloat z01 = minorRadius * std::sin(v0);
 
            glNormal3f(n0[0], n0[1], n0[2]);
            glTexCoord2f(static_cast<GLfloat>(j) / numMinor, static_cast<GLfloat>(i) / numMajor); 
            glVertex3f(x00, y00, z00);
            glNormal3f(n1[0], n1[1], n1[2]);
            glTexCoord2f(static_cast<GLfloat>(j) / numMinor, static_cast<GLfloat>(i + 1) / numMajor);  
            glVertex3f(x01, y01, z01);
        }
        glEnd();
    }
}
void renderScene() {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glPushMatrix();    
        
    glTranslatef(0.0F, 0.0F, zPos);
    glRotatef(xRot, 1.0F, 0.0F, 0.0F);
    glRotatef(yRot, 0.0F, 1.0F, 0.0F);
    glRotatef(zRot, 0.0F, 0.0F, 1.0F);

    glBindTexture(GL_TEXTURE_2D, textureIds[0]);
    drawTorus(1.0F, 0.3F, 32, 16);
    
    glPopMatrix();

    glutSwapBuffers();
}

void setupRc() {

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);

    // 开启2d纹理
    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

    glGenTextures(NUM_TEXTURES, textureIds);
    for (GLuint i = 0; i < NUM_TEXTURES; ++i) {
        glBindTexture(GL_TEXTURE_2D, textureIds[i]);

        // 加载纹理数据
        std::ifstream file(textureFiles[i], std::ios::binary);
        if (!file.is_open()) {
            throw std::runtime_error("Failed to open " + textureFiles[i]);
        }
        TargaHeader header;
        file.read(reinterpret_cast<char*>(&header), sizeof(TargaHeader));
        if(file.fail()) {
            throw std::runtime_error("Failed to read Targa header from " + textureFiles[i]);
        }
        std::cout << "Targa header: " << header << std::endl;
        const std::size_t imageSize = header.width * header.height * (header.bitsPerPixel / 8); 
        std::vector<char> imageData(imageSize);
        file.read(imageData.data(), imageSize);
        if(file.fail()) {
            throw std::runtime_error("Failed to read Targa image data from " + textureFiles[i]);
        }
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, header.width, header.height, GL_BGR, GL_UNSIGNED_BYTE, imageData.data());

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    }
}
void shutdownRc() {
    glDeleteTextures(NUM_TEXTURES, textureIds);
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
    std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case 27:    // ESC 键
        case 'q':
        case 'Q':
            std::cout << "keyboard close..." << "\n";
            std::exit(0);
            break;
        case 'a':
        case 'A':
            yRot -= 5.0F;
            break;
        case 'd':
        case 'D':
            yRot += 5.0F;
            // glutPostRedisplay();
            break;
        case 'w':
        case 'W':
            xRot -= 5.0F;
            break;
        case 's':
        case 'S':
            xRot += 5.0F;
            break;
        case 'z':
        case 'Z':
            zRot -= 5.0F;
            break;
        case 'x':
        case 'X':
            zRot += 5.0F;
            break;
        case 'c':
        case 'C':
            break;
        case 'v':
        case 'V':
            break;
        default:
            break;
    }
    glutPostRedisplay();
}
 
void specialKeys(const int key, const int x, const int y) {
    std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y) << "\n";
    switch (key) {
        case GLUT_KEY_UP:
            zPos += 1.0f;
            break;
        case GLUT_KEY_DOWN:
            zPos -= 1.0f;
            break;
        case GLUT_KEY_LEFT:
            break;
        case GLUT_KEY_RIGHT:
            break;
        default:
            break;
    }
    // Refresh the Window
    glutPostRedisplay();
}

void processMenu(const int value) {
    std::cout << std::format("Menu selected: {}", value) << "\n";
    for(GLuint i = 0; i < NUM_TEXTURES; ++i) {
        glBindTexture(GL_TEXTURE_2D, textureIds[i]);
        switch (value) {
            case 0:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                break;
            case 1:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                break;
            case 2:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
                break;
            case 3:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
                break;
            case 4:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
                break;
            case 5:
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
                break;
        }
    }
    glutSetWindow(mainWindowId);
    glutPostRedisplay();
}

auto main(const int argc, const char** argv) -> int {
    std::cout << "Hello, World!" << std::endl;
    glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    mainWindowId = glutCreateWindow("OpenGL");
    if (!mainWindowId) {
        throw std::runtime_error("Failed to create OpenGL window");
    }
    glutDisplayFunc(renderScene);
    glutReshapeFunc(changeSize);
    glutSpecialFunc(specialKeys);   // 特殊键事件
    glutKeyboardFunc(keyboard);  // 普通键事件

    glutCreateMenu(processMenu);
    glutAddMenuEntry("GL_NEAREST",0);
    glutAddMenuEntry("GL_LINEAR",1);
    glutAddMenuEntry("GL_NEAREST_MIPMAP_NEAREST",2);
    glutAddMenuEntry("GL_NEAREST_MIPMAP_LINEAR", 3);
    glutAddMenuEntry("GL_LINEAR_MIPMAP_NEAREST", 4);
    glutAddMenuEntry("GL_LINEAR_MIPMAP_LINEAR", 5);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    setupRc();
    glutMainLoop();
    return 0;
}
posted @ 2026-09-27 18:40  爱情丶眨眼而去  阅读(6)  评论(0)    收藏  举报