基于OpenGL实现三维室内场景的方案

一、环境搭建与基础配置

1.1 开发环境配置

// Visual Studio 2022配置示例
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>

// 初始化GLFW
if(!glfwInit()) {
    std::cerr << "Failed to initialize GLFW" << std::endl;
    return -1;
}

// 创建窗口
GLFWwindow* window = glfwCreateWindow(1280, 720, "室内场景", nullptr, nullptr);
glfwMakeContextCurrent(window);

// 初始化GLEW
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK) {
    std::cerr << "Failed to initialize GLEW" << std::endl;
    return -1;
}

1.2 视口与投影矩阵

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    glViewport(0, 0, width, height);
    glm::mat4 projection = glm::perspective(glm::radians(45.0f), 
                                         (float)width/height, 
                                         0.1f, 100.0f);
    // 传递投影矩阵到着色器
}

二、场景建模实现

2.1 基础几何体构建

// 墙体定义(使用VAO/VBO)
float walls[] = {
    // 位置          // 法线        // UV坐标
    -5.0f,  3.0f, -5.0f,  0.0f, 0.0f, 0.0f, 0.0f, // 左下前
     5.0f,  3.0f, -5.0f,  0.0f, 0.0f, 1.0f, 0.0f, // 右下前
     5.0f,  3.0f,  5.0f,  0.0f, 0.0f, 1.0f, 1.0f, // 右上后
    -5.0f,  3.0f,  5.0f,  0.0f, 0.0f, 0.0f, 1.0f  // 左上后
};

GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);

glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(walls), walls, GL_STATIC_DRAW);

// 顶点属性配置
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(3*sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(6*sizeof(float)));
glEnableVertexAttribArray(2);

2.2 复杂家具建模

// 沙发模型(使用OBJ加载器)
std::vector<float> sofa_vertices = loadOBJ("sofa.obj");
GLuint sofaVAO, sofaVBO;
glGenVertexArrays(1, &sofaVAO);
glGenBuffers(1, &sofaVBO);
glBindBuffer(GL_ARRAY_BUFFER, sofaVBO);
glBufferData(GL_ARRAY_BUFFER, sofa_vertices.size()*sizeof(float), &sofa_vertices[0], GL_STATIC_DRAW);

三、光照与材质系统

3.1 Phong光照模型

// 顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;

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

void main() {
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = mat3(transpose(inverse(model))) * aNormal;
    TexCoords = aTexCoords;
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

// 片段着色器
#version 330 core
out vec4 FragColor;

in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;

uniform sampler2D texture1;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;

void main() {
    // 环境光
    vec3 ambient = 0.1 * lightColor;
    
    // 漫反射
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;
    
    // 镜面反射
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = 0.5 * spec * lightColor;
    
    vec3 result = (ambient + diffuse + specular) * texture(texture1, TexCoords).rgb;
    FragColor = vec4(result, 1.0);
}

3.2 材质属性设置

// 材质结构体
struct Material {
    glm::vec3 ambient;
    glm::vec3 diffuse;
    glm::vec3 specular;
    float shininess;
};

// 传递材质参数
glUniform3fv(glGetUniformLocation(shaderProgram, "material.ambient"), 1, &material.ambient[0]);
glUniform3fv(glGetUniformLocation(shaderProgram, "material.diffuse"), 1, &material.diffuse[0]);
glUniform3fv(glGetUniformLocation(shaderProgram, "material.specular"), 1, &material.specular[0]);
glUniform1f(glGetUniformLocation(shaderProgram, "material.shininess"), material.shininess);

四、纹理映射系统

4.1 纹理加载与绑定

// 使用stb_image加载纹理
GLuint loadTexture(const char* path) {
    GLuint textureID;
    glGenTextures(1, &textureID);
    
    int width, height, nrChannels;
    unsigned char *data = stbi_load(path, &width, &height, &nrChannels, 0);
    if(data) {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    stbi_image_free(data);
    
    // 纹理参数设置
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    return textureID;
}

// 绑定纹理到着色器
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);

4.2 环境贴图实现

// 立方体贴图加载
GLuint loadCubemap(vector<std::string> faces) {
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

    int width, height, nrChannels;
    for(uint i = 0; i < faces.size(); i++) {
        unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
        if(data) {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
            stbi_image_free(data);
        }
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    return textureID;
}

参考代码 采用openGL实现的一个三维室内场景 www.youwenfan.com/contentcnh/56182.html

五、交互系统实现

5.1 第一人称视角控制

// 摄像机控制类
class Camera {
public:
    glm::vec3 Position;
    glm::vec3 Front;
    glm::vec3 Up;
    glm::vec3 Right;
    glm::vec3 WorldUp;

    float Yaw;
    float Pitch;

    Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f), 
           glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), 
           float yaw = -90.0f, float pitch = 0.0f) {
        Position = position;
        WorldUp = up;
        Yaw = yaw;
        Pitch = pitch;
        updateCameraVectors();
    }

    void ProcessMouseMovement(float xoffset, float yoffset) {
        xoffset *= sensitivity;
        yoffset *= sensitivity;

        Yaw += xoffset;
        Pitch += yoffset;

        if(Pitch > 89.0f) Pitch = 89.0f;
        if(Pitch < -89.0f) Pitch = -89.0f;

        updateCameraVectors();
    }

private:
    void updateCameraVectors() {
        glm::vec3 front;
        front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
        front.y = sin(glm::radians(Pitch));
        front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
        Front = glm::normalize(front);
    }
};

5.2 物体交互系统

// 碰撞检测(AABB算法)
struct AABB {
    glm::vec3 min;
    glm::vec3 max;
};

bool CheckCollision(AABB a, AABB b) {
    return (a.min.x <= b.max.x && a.max.x >= b.min.x) &&
           (a.min.y <= b.max.y && a.max.y >= b.min.y) &&
           (a.min.z <= b.max.z && a.max.z >= b.min.z);
}

// 物体移动控制
void moveObject(glm::vec3& position, glm::vec3 direction, float speed) {
    if(!CheckCollision(objectAABB, cameraAABB)) {
        position += direction * speed;
    }
}
posted @ 2025-09-18 11:03  老夫写代码  阅读(11)  评论(0)    收藏  举报