OpenGL 3D纹理

理论基础

3D纹理(也称为体积纹理)是纹理映射的扩展,从2D平面扩展到3D空间。与2D纹理不同,3D纹理在三个维度上存储数据(宽度、高度和深度),允许在整个3D空间中采样,而不仅仅是在平面上。

3D纹理的主要特性和用途:

  1. 体积数据表示:用于表示完整的3D数据集,如医学扫描(CT、MRI)、气象数据等
  2. 空间采样:允许在3D空间中的任意位置进行纹理采样
  3. 层次细节:支持类似2D纹理的MipMap功能,但在三维空间中
  4. 程序化噪声:用于创建自然现象如云、烟雾、火焰等效果
  5. 体积渲染技术:用于光线投射、体积密度等高级渲染技术

代码实现

下面是一个使用GLFW和GLAD实现3D纹理的详细示例,包括如何创建、填充和使用3D纹理:

cpp 复制代码
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <cmath>
#include <vector>

// 窗口尺寸
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// 着色器程序
unsigned int shaderProgram;

// 3D纹理ID
unsigned int texture3D;

// 控制参数
float slicePosition = 0.5f; // 切片位置 (0.0 - 1.0)
bool animateSlice = true;   // 是否动画切片位置

// 顶点着色器源码
const char *vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aTexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 TexCoord;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}
)";

// 片段着色器源码
const char *fragmentShaderSource = R"(
#version 330 core
out vec4 FragColor;
in vec3 TexCoord;

uniform sampler3D texture3D;
uniform float slicePosition; // 在特定的z位置进行切片

void main()
{
    // 基本的3D纹理采样
    vec4 texColor = texture(texture3D, vec3(TexCoord.xy, slicePosition));
    
    // 可视化深度值
    float depthValue = abs(TexCoord.z - slicePosition);
    float alpha = 1.0 - smoothstep(0.0, 0.01, depthValue); // 在切片附近设置透明度
    
    // 最终颜色
    FragColor = vec4(texColor.rgb, texColor.a * alpha);
}
)";

// 编译着色器
unsigned int compileShader(const char* source, GLenum type) {
    unsigned int shader = glCreateShader(type);
    glShaderSource(shader, 1, &source, NULL);
    glCompileShader(shader);
    
    int success;
    char infoLog[512];
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(shader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    
    return shader;
}

// 创建着色器程序
unsigned int createShaderProgram(const char* vertexSource, const char* fragmentSource) {
    unsigned int vertexShader = compileShader(vertexSource, GL_VERTEX_SHADER);
    unsigned int fragmentShader = compileShader(fragmentSource, GL_FRAGMENT_SHADER);
    
    unsigned int program = glCreateProgram();
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    glLinkProgram(program);
    
    int success;
    char infoLog[512];
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(program, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }
    
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    
    return program;
}

// 创建3D纹理
void create3DTexture() {
    // 3D纹理的尺寸
    const int width = 64;
    const int height = 64;
    const int depth = 64;
    
    // 创建纹理数据 - 这里我们创建一个程序化的3D噪声纹理
    std::vector<unsigned char> textureData(width * height * depth * 4); // RGBA格式
    
    // 生成3D噪声数据
    for (int z = 0; z < depth; z++) {
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                // 计算当前体素的索引
                int index = (z * width * height + y * width + x) * 4;
                
                // 归一化坐标 (0.0 - 1.0)
                float nx = static_cast<float>(x) / width;
                float ny = static_cast<float>(y) / height;
                float nz = static_cast<float>(z) / depth;
                
                // 简单的3D噪声函数
                float noise = 0.5f + 0.5f * sin(nx * 10) * sin(ny * 10) * sin(nz * 10);
                
                // 添加一些距离场效果
                float centerX = 0.5f;
                float centerY = 0.5f;
                float centerZ = 0.5f;
                
                float distance = std::sqrt(
                    (nx - centerX) * (nx - centerX) +
                    (ny - centerY) * (ny - centerY) +
                    (nz - centerZ) * (nz - centerZ)
                );
                
                // 创建一个球体
                float sphereRadius = 0.35f;
                float sphereValue = 1.0f - smoothstep(sphereRadius - 0.05f, sphereRadius + 0.05f, distance);
                
                // 混合噪声和球体效果
                float finalValue = noise * sphereValue;
                
                // 设置颜色,基于位置和噪声值
                textureData[index + 0] = static_cast<unsigned char>((nx * finalValue) * 255); // R
                textureData[index + 1] = static_cast<unsigned char>((ny * finalValue) * 255); // G
                textureData[index + 2] = static_cast<unsigned char>((nz * finalValue) * 255); // B
                textureData[index + 3] = static_cast<unsigned char>(finalValue * 255);       // A
            }
        }
    }
    
    // 创建OpenGL纹理对象
    glGenTextures(1, &texture3D);
    glBindTexture(GL_TEXTURE_3D, texture3D);
    
    // 设置纹理参数
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    // 上传纹理数据
    glTexImage3D(
        GL_TEXTURE_3D,
        0,                  // Mipmap级别
        GL_RGBA,            // 内部格式
        width,              // 宽度
        height,             // 高度
        depth,              // 深度
        0,                  // 边框
        GL_RGBA,            // 数据格式
        GL_UNSIGNED_BYTE,   // 数据类型
        textureData.data()  // 数据指针
    );
    
    // 生成Mipmap
    glGenerateMipmap(GL_TEXTURE_3D);
    
    // 解绑纹理
    glBindTexture(GL_TEXTURE_3D, 0);
    
    std::cout << "3D Texture created with dimensions: " << width << "x" << height << "x" << depth << std::endl;
}

// 创建矩阵结构体和辅助函数
struct Matrix4 {
    float m[16];
    
    Matrix4() {
        // 初始化为单位矩阵
        for (int i = 0; i < 16; i++)
            m[i] = 0.0f;
        m[0] = m[5] = m[10] = m[15] = 1.0f;
    }
    
    // 简单的矩阵乘法
    Matrix4 operator*(const Matrix4& other) {
        Matrix4 result;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                result.m[i*4+j] = 0.0f;
                for (int k = 0; k < 4; k++) {
                    result.m[i*4+j] += m[i*4+k] * other.m[k*4+j];
                }
            }
        }
        return result;
    }
};

// 创建平移矩阵
Matrix4 translate(float x, float y, float z) {
    Matrix4 result;
    result.m[12] = x;
    result.m[13] = y;
    result.m[14] = z;
    return result;
}

// 创建旋转矩阵 (绕各轴旋转)
Matrix4 rotate(float angle, float x, float y, float z) {
    Matrix4 result;
    
    float c = cos(angle);
    float s = sin(angle);
    float t = 1.0f - c;
    
    // 归一化轴向量
    float len = sqrt(x*x + y*y + z*z);
    if (len > 0.0001f) {
        x /= len;
        y /= len;
        z /= len;
    }
    
    result.m[0] = t*x*x + c;
    result.m[1] = t*x*y + s*z;
    result.m[2] = t*x*z - s*y;
    
    result.m[4] = t*x*y - s*z;
    result.m[5] = t*y*y + c;
    result.m[6] = t*y*z + s*x;
    
    result.m[8] = t*x*z + s*y;
    result.m[9] = t*y*z - s*x;
    result.m[10] = t*z*z + c;
    
    return result;
}

// 创建透视投影矩阵
Matrix4 perspective(float fov, float aspect, float near, float far) {
    Matrix4 result;
    float tanHalfFov = tan(fov / 2.0f);
    
    result.m[0] = 1.0f / (aspect * tanHalfFov);
    result.m[5] = 1.0f / tanHalfFov;
    result.m[10] = -(far + near) / (far - near);
    result.m[11] = -1.0f;
    result.m[14] = -(2.0f * far * near) / (far - near);
    result.m[15] = 0.0f;
    
    return result;
}

// 键盘回调函数处理控制
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (action == GLFW_PRESS) {
        switch (key) {
            case GLFW_KEY_SPACE:
                // 切换切片动画
                animateSlice = !animateSlice;
                std::cout << "Slice Animation: " << (animateSlice ? "Enabled" : "Disabled") << std::endl;
                break;
            case GLFW_KEY_UP:
                // 增加切片位置
                slicePosition = std::min(slicePosition + 0.05f, 1.0f);
                std::cout << "Slice Position: " << slicePosition << std::endl;
                break;
            case GLFW_KEY_DOWN:
                // 减少切片位置
                slicePosition = std::max(slicePosition - 0.05f, 0.0f);
                std::cout << "Slice Position: " << slicePosition << std::endl;
                break;
        }
    }
}

int main() {
    // 初始化GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
    // 创建窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "3D Texture Example", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);
    
    // 初始化GLAD
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    
    // 设置视口
    glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
    
    // 创建着色器程序
    shaderProgram = createShaderProgram(vertexShaderSource, fragmentShaderSource);
    
    // 创建3D纹理
    create3DTexture();
    
    // 启用深度测试和Alpha混合
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    // 定义一个立方体,用于显示3D纹理
    // 立方体的每个顶点包含位置和纹理坐标 (x,y,z, tx,ty,tz)
    float vertices[] = {
        // 前面 (z = 1.0)
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f,
        
        // 后面 (z = -1.0)
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
        
        // 左面 (x = -0.5)
        -0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  1.0f, 0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  1.0f, 0.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 1.0f,
        
        // 右面 (x = 0.5)
         0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 1.0f,
        
        // 底面 (y = -0.5)
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
        
        // 顶面 (y = 0.5)
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f
    };
    
    // 设置VAO和VBO
    unsigned int VAO, VBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    // 位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 纹理坐标属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    
    // 解绑
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    
    // 输出控制说明
    std::cout << "Controls:" << std::endl;
    std::cout << "  SPACE - Toggle slice animation" << std::endl;
    std::cout << "  UP/DOWN - Manually adjust slice position" << std::endl;
    
    // 渲染循环
    while (!glfwWindowShouldClose(window)) {
        // 处理输入
        if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
            glfwSetWindowShouldClose(window, true);
        
        // 更新切片位置(如果动画启用)
        if (animateSlice) {
            float time = (float)glfwGetTime();
            slicePosition = 0.5f + 0.5f * sin(time * 0.5f);
        }
        
        // 清空颜色和深度缓冲
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        // 激活着色器
        glUseProgram(shaderProgram);
        
        // 创建变换矩阵
        float time = (float)glfwGetTime();
        
        // 模型矩阵 - 旋转立方体
        Matrix4 model = rotate(time * 0.5f, 0.0f, 1.0f, 0.0f) * rotate(time * 0.3f, 1.0f, 0.0f, 0.0f);
        
        // 视图矩阵 - 向后移动相机
        Matrix4 view = translate(0.0f, 0.0f, -2.0f);
        
        // 投影矩阵
        float fov = 45.0f * 3.14159f / 180.0f; // 转换为弧度
        float aspect = (float)SCR_WIDTH / (float)SCR_HEIGHT;
        Matrix4 projection = perspective(fov, aspect, 0.1f, 100.0f);
        
        // 传递变换矩阵到着色器
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, model.m);
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, view.m);
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, projection.m);
        
        // 绑定3D纹理
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, texture3D);
        glUniform1i(glGetUniformLocation(shaderProgram, "texture3D"), 0);
        
        // 传递切片位置
        glUniform1f(glGetUniformLocation(shaderProgram, "slicePosition"), slicePosition);
        
        // 绘制立方体
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        
        // 交换缓冲并检查事件
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    
    // 清理资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteTextures(1, &texture3D);
    glDeleteProgram(shaderProgram);
    
    glfwTerminate();
    return 0;
}

代码解析

这个示例实现了一个完整的3D纹理渲染系统,包含以下关键组件:

  1. 3D纹理创建与填充

    • 使用glTexImage3D函数创建3D纹理
    • 程序化生成纹理数据,创建一个包含噪声和球体效果的体积数据
    • 设置纹理参数和生成Mipmap,与2D纹理类似但多了一个R维度的设置
  2. 着色器实现

    • 顶点着色器传递3D纹理坐标
    • 片段着色器使用sampler3D采样3D纹理,基于插值的纹理坐标
    • 实现了一个简单的"切片"效果,只显示特定深度层的纹理数据
  3. 交互控制

    • 空格键切换切片位置的动画
    • 上下箭头键手动调整切片位置,观察不同深度处的纹理数据
  4. 渲染技术

    • 使用立方体作为3D纹理的可视化容器
    • 应用Alpha混合显示半透明效果
    • 使用深度测试确保正确的渲染顺序

3D纹理的高级应用

  1. 体积渲染

    • 医学成像:显示3D扫描数据,如CT和MRI
    • 科学可视化:显示密度、温度等体积数据
    • 地形分析:显示地下结构、地质层
  2. 程序化纹理和特效

    • 云、烟、雾等体积效果
    • 3D噪声图和程序化体积纹理
    • 颗粒材质和多孔物质的模拟
  3. 高级渲染技术

    • 光线行进(Ray Marching):通过3D纹理计算光线吸收和散射
    • 隐式曲面:使用3D纹理定义体积密度函数
    • 体积光照:通过3D纹理计算体积内的光线行为
  4. 优化技术

    • 多分辨率3D纹理:类似于2D纹理的Mipmap
    • 块压缩:减少内存占用
    • 光线采样优化:根据视角和重要性调整采样率

性能注意事项

  1. 内存占用

    • 3D纹理可能占用大量内存 (width × height × depth × 字节数/像素)
    • 较大的3D纹理可能需要压缩或分块处理
  2. 采样性能

    • 3D纹理采样比2D纹理采样更昂贵
    • 在片段着色器中过度采样可能导致性能问题
  3. 缓存优化

    • 考虑3D纹理的内存布局以优化缓存使用
    • 连续访问同一区域的纹理以提高缓存命中率
  4. 功能支持

    • 确保目标硬件和驱动程序支持所需的3D纹理功能
    • 较老的硬件可能有3D纹理大小或功能的限制

这个示例提供了一个坚实的3D纹理基础,可以根据特定需求进一步扩展和优化。例如,可以实现更复杂的体积渲染算法、添加光线行进技术或创建更复杂的程序化3D纹理。

相关推荐
CoovallyAIHub1 小时前
如何用更少的内存训练你的PyTorch模型?深度学习GPU内存优化策略总结
pytorch·深度学习·性能优化
xcLeigh12 小时前
WPF高级 | WPF 3D 图形编程基础:创建立体的用户界面元素
ui·3d·c#·wpf
若疆赤云online21 小时前
3dtiles
3d
DFT计算杂谈1 天前
VASPKIT 3D 能带计算及动态3D能带绘图脚本
linux·运维·开发语言·python·ubuntu·3d
挣扎与觉醒中的技术人1 天前
如何本地部署大模型及性能优化指南(附避坑要点)
人工智能·opencv·算法·yolo·性能优化·audiolm
井队Tell1 天前
打造高清3D虚拟世界|零基础学习Unity HDRP高清渲染管线(第一天)
学习·3d·unity
supermapsupport1 天前
SuperMap iClient3D for WebGL三维场景与二维地图联动
3d·webgl
少说多想勤做2 天前
【前沿 热点 顶会】CVPR 2025 录用的与图像|视频恢复、抠图、超分辨率、3D生成有关的论文
人工智能·3d·音视频·视频·超分辨率重建·图像恢复