【OpenGL】Learn OpenGL Note

参考资料:

https://learnopengl-cn.github.io/01%20Getting%20started/01%20OpenGL/

1.绘制三角形

本节关键词

  • 顶点数组对象:Vertex Array Object(VAO)

2.着色器

3.纹理(Texture)

3.1 纹理简介

我们可以为每个顶点添加颜色来增加图形的细节,从而创建出有趣的图像。但是,如果想让图形看起来更真实,我们就必须有足够多的顶点,从而指定足够多的颜色。这将会产生很多额外开销,因为每个模型都会需求更多的顶点,每个顶点又需求一个颜色属性。

纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节;你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的3D的房子上,这样你的房子看起来就像有砖墙外表了。因为我们可以在一张图片上插入非常多的细节,这样就可以让物体非常精细而不用指定额外的顶点。

为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(译注:采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。

纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终止于(1, 1),即纹理图片的右上角。

我们为三角形指定了3个纹理坐标点。如上图所示,我们希望三角形的左下角对应纹理的左下角,因此我们把三角形左下角顶点的纹理坐标设置为(0, 0);同理右下方的顶点设置为(1, 0);三角形的上顶点对应于图片的上中位置所以我们把它的纹理坐标设置为(0.5, 1.0)。我们只要给顶点着色器传递这三个纹理坐标就行了,接下来它们会被传到片段着色器中,它会为每个片段进行纹理坐标的插值。

纹理坐标如下:

float texCoords[] = {
  0.0f,0.0f,    // 左下角
  1.0f,0.0f,    // 右下角
  0.5f,1.0f     // 上中
};

对纹理采样的解释非常宽松,它可以采用几种不同的插值方式。所以我们需要自己告诉OpenGL该怎样对纹理 采样

3.2 纹理环绕方式

3.3 纹理过滤

3.4 加载和创建纹理

3.5 生成纹理

3.6 应用纹理

3.7 纹理单元

3.8 stb_image

stb_image.h 是一个单文件公共领域(public domain)的图像加载库,由 Sean Barrett 编写。这个库提供了一个简单的方式来加载多种图像格式,如 JPEG、PNG、TGA、BMP、GIF、HDR 等,并将它们转换为原始像素数据。stb_image.h 非常适合在游戏、图形应用程序和其他需要图像加载功能的项目中使用。

3.8.1 特点

  • 单文件库:整个库只包含一个头文件,易于集成到项目中。
  • 多种图像格式支持:支持多种常见的图像格式。
  • 简单易用:API 简单,易于理解和使用。
  • 公共领域:没有许可证限制,可以自由地在任何项目中使用。

3.8.2 使用方法

要使用 stb_image.h,你只需要将这个头文件包含到你的项目中,并定义STB_IMAGE_IMPLEMENTATION 宏来生成库的实现。以下是一个基本的使用示例:

1.下载仓库

下载 stb_image.h:

你可以从 Sean Barrett 的 GitHub 仓库下载 stb_image.h:

git clone https://github.com/nothings/stb.git

2.包含头文件

在你的源文件中包含 stb_image.h,并定义 STB_IMAGE_IMPLEMENTATION 宏来生成实现:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

3.加载图像

使用 stbi_load 函数加载图像文件:

int width, height, channels;
unsigned char* data = stbi_load("path_to_image.jpg", &width, &height, &channels, 0);
if (data) {
    // 图像加载成功,data 包含图像的像素数据
} else {
    // 图像加载失败
}

4.释放图像数据

使用 stbi_image_free 函数释放图像数据:

stbi_image_free(data);

5.使用实例

在OpenGL中使用stb_image.h加载纹理

#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

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

const char* fragmentShaderSource = R"(
        #version 330 core
        out vec4 FragColor;

        in vec2 TexCoord;

        uniform sampler2D ourTexture;

        void main() {
            FragColor = texture(ourTexture, TexCoord);
        }
        )";

int main() {
    glfwInit();
    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Texture Example", nullptr, nullptr);
    if (!window) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cerr << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // 创建顶点数组对象和顶点缓冲对象
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

#if 1
    GLfloat vertices[] = {
        // 位置         // 纹理坐标
        -0.5f,  0.0f, 0.0f, 0.0f, 0.0f,
         0.5f,  0.0f, 0.0f, 1.0f, 0.0f,
         0.5f,  0.5f, 0.0f, 1.0f, 1.0f,
        -0.5f,  0.5f, 0.0f, 0.0f, 1.0f
    };
#endif

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 纹理坐标属性
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // 创建并编译顶点着色器
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    // 创建并编译片段着色器
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    // 创建着色器程序并链接
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // 加载纹理
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    // 设置纹理参数
    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);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // 加载图像文件
    int width = 533, height = 300, nrChannels = 3;
    // 在加载图像时将图像在垂直方向上进行翻转。
    // OpenGL 中,纹理坐标的原点通常在左下角,而很多图像的原点在左上角,避免图像上下翻转
    stbi_set_flip_vertically_on_load(true);
    // 使用stbi_load函数加载指定路径的图片,并获取图片的宽度、高度和通道数。最后一个参数4表示强制将图片加载为RGBA格式
    unsigned char* data = stbi_load("A:/codes/OpenGL/OpenGLDemo1/x64/Debug/123.jpg", &width, &height, &nrChannels, 4);
    if (data) {
        // 使用glTexImage2D函数将图片数据转换为OpenGL纹理。GL_TEXTURE_2D表示二维纹理,
        // GL_RGBA表示纹理的内部格式为RGBA,width和height是纹理的宽度和高度,GL_RGBA表示纹理数据的格式,
        // GL_UNSIGNED_BYTE表示每个颜色分量的类型为无符号字节
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        // 使用glGenerateMipmap函数为纹理生成Mipmap,以提高纹理在不同缩放比例下的显示效果
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else {
        std::cerr << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);

    // 渲染循环
    while (!glfwWindowShouldClose(window)) {
        // 清除当前绑定的帧缓冲区的颜色缓冲区,通常用于在每次绘制新帧之前清除屏幕。
        glClear(GL_COLOR_BUFFER_BIT);
        // 激活一个着色器程序(shaderProgram),这个程序包含了顶点着色器和片段着色器,用于定义OpenGL如何渲染图形。
        glUseProgram(shaderProgram);
        // 绑定一个顶点数组对象(VAO),这个对象存储了顶点数据的格式和顶点缓冲区对象(VBOs)的状态。
        glBindVertexArray(VAO);
        // 激活纹理单元0,这是告诉OpenGL你将使用哪个纹理单元来绑定纹理。
        glActiveTexture(GL_TEXTURE0);
        // 将一个2D纹理绑定到当前激活的纹理单元上。这里的texture是一个纹理对象的句柄。
        glBindTexture(GL_TEXTURE_2D, texture);
        // 将一个整数值(这里是0)传递给着色器程序中的一个名为ourTexture的uniform变量。这个uniform通常用于告诉着色器程序使用哪个纹理单元。
        glUniform1i(glGetUniformLocation(shaderProgram, "ourTexture"), 0);
        // 绘制一个由4个顶点组成的三角形扇形(GL_TRIANGLE_FAN)。这里的0是起始顶点的索引,4是顶点的数量。
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
        // 交换前后缓冲区,这样绘制的内容就可以在屏幕上显示出来了。
        glfwSwapBuffers(window);
        // 处理事件队列中的所有事件,例如键盘输入、鼠标移动等。
        glfwPollEvents();
    }

    // 清理资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);
    glDeleteTextures(1, &texture);

    glfwTerminate();
    return 0;
}

3.9 纹理的具体使用

3.9.1 Texture 2d

在 OpenGL 中,纹理(Texture)是一种将图像数据映射到图形对象上的技术,可以用来增加图形的真实感和细节。<span class="ne-text">Texture 2D</span> 是最常见的纹理类型,用于将二维图像映射到几何形状上。以下是使用 <span class="ne-text">Texture 2D</span> 的基本步骤:

3.9.1.1 步骤 1: 加载纹理图像

**首先,你需要加载一个图像文件并将其数据读入内存。可以使用库如 **<span class="ne-text">stb_image</span><span class="ne-text">SOIL</span><span class="ne-text">FreeImage</span> 来加载图像文件。

3.9.1.2 步骤 2: 生成纹理对象

**使用 **<span class="ne-text">glGenTextures</span> 函数生成一个纹理对象的名称。

GLuint textureID;
glGenTextures(1, &textureID);

3.9.1.3 步骤 3: 绑定纹理对象

**使用 **<span class="ne-text">glBindTexture</span> 函数将纹理对象绑定到 <span class="ne-text">GL_TEXTURE_2D</span> 目标上。

glBindTexture(GL_TEXTURE_2D, textureID);

3.9.1.4 步骤 4: 设置纹理参数

**使用 **<span class="ne-text">glTexParameteri</span> 函数设置纹理的参数,如纹理的环绕方式和过滤方式。

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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

3.9.1.5 步骤 5: 上传纹理图像数据

**使用 **<span class="ne-text">glTexImage2D</span> 函数将图像数据上传到 GPU。

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData);

3.9.1.6 步骤 6: 生成 mipmaps(可选)

**使用 **<span class="ne-text">glGenerateMipmap</span> 函数生成纹理的 mipmap。

glGenerateMipmap(GL_TEXTURE_2D);

3.9.1.7 步骤 7: 在着色器中使用纹理

在顶点着色器和片段着色器中声明纹理采样器,并在片段着色器中使用它来获取纹理颜色。

顶点着色器(vertex_shader.glsl)
#version 330 core

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

void main() {
    gl_Position = vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}
片段着色器(fragment_shader.glsl)
#version 330 core

out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2D ourTexture;

void main() {
    FragColor = texture(ourTexture, TexCoord);
}

3.9.1.8 步骤 8: 绑定纹理并渲染

在渲染循环中,绑定纹理对象并绘制几何形状。

glBindTexture(GL_TEXTURE_2D, textureID);
// 绘制几何形状

3.9.1.9 示例代码

**以下是一个完整的示例,展示了如何在 OpenGL 中使用 **<span class="ne-text">Texture 2D</span>

#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

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

const char* fragmentShaderSource = R"(
#version 330 core
out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2D ourTexture;

void main() {
    FragColor = texture(ourTexture, TexCoord);
}
)";

int main() {
    glfwInit();
    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Texture Example", nullptr, nullptr);
    if (!window) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cerr << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // 创建顶点数组对象和顶点缓冲对象
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    GLfloat vertices[] = {
        // 位置         // 纹理坐标
        -0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
         0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
         0.5f,  0.5f, 0.0f, 1.0f, 1.0f,
        -0.5f,  0.5f, 0.0f, 0.0f, 1.0f
    };
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 纹理坐标属性
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // 创建并编译顶点着色器
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    // 创建并编译片段着色器
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    // 创建着色器程序并链接
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // 加载纹理
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    // 设置纹理参数
    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);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // 加载图像文件
    int width = 533, height = 300, nrChannels = 4;
    stbi_set_flip_vertically_on_load(true);
    unsigned char* data = stbi_load("A:/codes/OpenGL/OpenGLDemo1/x64/Debug/123.jpg", &width, &height, &nrChannels, 4);
    if (data) {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else {
        std::cerr << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);

    // 渲染循环
    while (!glfwWindowShouldClose(window)) { 
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        glUniform1i(glGetUniformLocation(shaderProgram, "ourTexture"), 0);

        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // 清理资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);
    glDeleteTextures(1, &texture);

    glfwTerminate();
    return 0;
}
注意事项
  • **确保你已经安装了必要的库,如 **<span class="ne-text">GLFW</span><span class="ne-text">GLAD</span><span class="ne-text">stb_image</span>
  • **替换 **<span class="ne-text">"path_to_your_texture.jpg"</span> 为你的纹理图像文件的实际路径。
  • 在使用纹理之前,确保正确设置纹理参数和上传纹理图像数据。
  • **在片段着色器中使用 **<span class="ne-text">sampler2D</span> 类型的变量来采样纹理。

**通过以上步骤,你可以在 OpenGL 中使用 **<span class="ne-text">Texture 2D</span> 来增强你的图形渲染效果。

3.9.2 Texture cubemap

在 OpenGL 中,立方体贴图(Texture Cubemap)是一种特殊的纹理,它由六个正方形面组成,每个面代表一个方向(正面、背面、左侧、右侧、顶部、底部)。立方体贴图通常用于环境映射(Environment Mapping),如反射和折射效果,以及天空盒(Skybox)的实现。

3.9.2.1 步骤1:准备立方体贴图的图像

准备六个图像文件,分别对应立方体的六个面。这些图像应该具有相同的尺寸和格式。

3.9.2.2 步骤2:创建立方体贴图的对象

使用 glGenTextures 创建一个纹理对象,并使用 glBindTexture 将其绑定到 GL_TEXTURE_CUBE_MAP 目标。

GLuint cubemapTexture;
glGenTextures(1, &cubemapTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);

3.9.2.3 步骤3:设置纹理参数

设置立方体贴图的参数纹理,如环绕方式和过滤方式

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

3.9.2.4 步骤4:上传纹理图像数据

使用glTexImage2D为立方体贴图的每个面上传图像数据

// 假设 faces[] 包含六个图像的路径
for (int i = 0; i < 6; ++i) {
    int width, height, nrChannels;
    unsigned char* data = stbi_load(faces[i], &width, &height, &nrChannels, 0);
    if (data) {
        // 使用glTexImage2D函数将图片数据转换为OpenGL纹理。GL_TEXTURE_2D表示二维纹理,
        // GL_RGBA表示纹理的内部格式为RGBA,width和height是纹理的宽度和高度,GL_RGBA表示纹理数据的格式,
        // GL_UNSIGNED_BYTE表示每个颜色分量的类型为无符号字节
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    } else {
        std::cerr << "Failed to load cubemap texture" << std::endl;
    }
    stbi_image_free(data);
}

3.9.2.5 步骤5:生成mipmap(可选)

如果需要,可以为立方体贴图生成 mipmap。

glGenerateMipmap(GL_TEXTURE_CUBE_MAP);

3.9.2.6 步骤6:在着色器中使用立方体贴图

在顶点着色器和片段着色器中声明立方体贴图采样器,并在片段着色器中使用它来获取纹理颜色。

顶点着色器

#version 330 core

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

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

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

片段着色器

#version 330 core

out vec4 FragColor;

in vec3 FragPos;
in vec3 Normal;

uniform samplerCube skybox;

void main() {
    vec3 I = normalize(FragPos - viewPos);
    vec3 R = reflect(I, normalize(Normal));
    FragColor = texture(skybox, R);
}

3.9.2.7 步骤7:绑定立方体贴图并渲染

在渲染循环中,绑定立方体贴图对象并绘制几何形状。

// 绘制几何形状
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);

3.9.2.8 使用示例

#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

const char* vertexShaderSource = R"(
#version 330 core
// 定义了一个输入变量  aPos  ,它是一个3D向量(  vec3  ),
// 用于存储顶点的位置。  layout (location = 0)  
// 指定了这个变量在顶点属性中的绑定位置,这里是0
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projecttion;

void main(){
    gl_Postion = projecttion * view * model * vec4(aPos,1.0);
    FragPos = vec3(model * vec4(aPos,1.0));
    Normal = mat3(transpose(inverse(model))) * aNormal;
}
)";


const char* fragmentShaderSource = R"(




)";



int main()
{



	GLuint cubemapTexture;
	glGenTextures(1, &cubemapTexture);
	glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);


}

3.9.3 Alpha Blending

在 OpenGL 中,Alpha Blending(alpha 混合)是一种用于在渲染过程中控制像素透明度的技术。它允许像素的颜色与已经存储在帧缓冲区中的颜色按照一定的比例进行混合,从而实现透明效果。

Alpha 混合通常涉及到两个主要的 OpenGL 函数:

1.glEnable(GL_BLEND):

启用混合模式。在渲染前,你需要调用这个函数来启用 alpha 混合。

2.glBlendFunc(GLenum sfactor, GLenum dfactor):

设置混合函数,定义了如何将源颜色(新绘制的像素)与目标颜色(帧缓冲区中已有的像素)进行混合。sfactor 参数定义了源颜色的权重,而 dfactor 参数定义了目标颜色的权重。

Alpha混合的基本用法:

// 启用混合
glEnable(GL_BLEND);

// 设置混合函数,常见的组合有:
// GL_SRC_ALPHA 和 GL_ONE_MINUS_SRC_ALPHA 用于将源颜色的 alpha 值用于混合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// 或者使用预定义的混合模式:
// glBlendFuncSeparate 允许分别设置 RGB 和 Alpha 通道的混合函数
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

Alpha 混合的工作方式:

当启用 alpha 混合时,每个像素的颜色值会根据其 alpha 值与帧缓冲区中已有的颜色值进行混合。混合的结果取决于 glBlendFunc 设置的混合因子。

例如,如果 glBlendFunc 设置为 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,则混合计算如下:

结果颜色 = 源颜色 * 源 alpha + 目标颜色 * (1 - 源 alpha)

这意味着,如果源像素的 alpha 值为 0,则完全不显示源像素;如果 alpha 值为 1,则完全不透明,只显示源像素。

注意事项:

  • Alpha 混合通常与纹理一起使用,纹理图像需要包含 alpha 通道(如 RGBA 格式)。
  • 在使用 alpha 混合时,确保纹理的格式包含 alpha 通道,并且在加载纹理时正确设置。
  • Alpha 混合可能会影响性能,因为它需要额外的计算。
  • 在某些情况下,可能需要调整混合函数以实现特定的视觉效果。

Alpha 混合是实现透明效果的关键技术,广泛应用于游戏开发、图形用户界面和各种视觉效果中。

posted @ 2025-05-18 23:09  Emma1111  阅读(27)  评论(0)    收藏  举报