OpenGL 入门(九)—Material(材质)和 光照贴图

news2024/11/19 21:14:03

文章目录

  • 材质
    • 设置材质
    • 光的属性
    • 脚本实现
  • 光照贴图
    • 漫反射贴图
    • 高光反射贴图

材质

材质本质是一个数据集,主要功能就是给渲染器提供数据和光照算法。

如果我们想要在OpenGL中模拟多种类型的物体,我们必须针对每种表面定义不同的材质(Material)属性。

我们可以分别为三个光照分量定义一个材质颜色(Material Color):环境光照(Ambient Lighting)漫反射光照(Diffuse Lighting)镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对表面的颜色输出有细粒度的控制了。

再添加一个反光度(Shininess)分量,设置材质属性:

#version 330 core
struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
}; 
//在片段着色器中,我们创建一个结构体(Struct)来储存物体的材质属性。
uniform Material material;

设置材质

可以通过设置适当的uniform来设置应用中物体。

GLSL中一个结构体在设置uniform时并无任何区别,结构体只是充当uniform变量们的一个命名空间。所以如果想填充这个结构体的话,我们必须设置每个单独的uniform,但要以结构体名为前缀:

lightingShader.setVec3("material.ambient",  1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.diffuse",  1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
lightingShader.setFloat("material.shininess", 32.0f);

光的属性

一个光源对它的ambient、diffuse和specular光照分量有着不同的强度。

  • 环境光照通常被设置为一个比较低的强度,因为我们不希望环境光颜色太过主导。
  • 光源的漫反射分量通常被设置为我们希望光所具有的那个颜色,通常是一个比较明亮的白色。
  • 镜面光分量通常会保持为vec3(1.0),以最大强度发光。注意我们也将光源的位置向量加入了结构体。

为光照属性创建类似材质结构体:

struct Light {
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Light light;

和材质uniform一样,我们需要更新片段着色器:

vec3 ambient  = light.ambient * material.ambient;
vec3 diffuse  = light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);

脚本实现

创建材质的顶点着色器和片元着色器

materials.vs

#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()
{
    FragPos = vec3(model * vec4(aPos, 1.0));
    //在顶点着色器中,我们可以使用inverse和transpose函数自己生成这个法线矩阵,这两个函数对所有类型矩阵都有效
    Normal = mat3(transpose(inverse(model))) * aNormal;  
    
    gl_Position = projection * view * vec4(FragPos, 1.0);
}

materials.fs

#version 330 core
out vec4 FragColor;

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;    
    float shininess;
}; 

struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

in vec3 FragPos;  
in vec3 Normal;  
  
uniform vec3 viewPos;
uniform Material material;
uniform Light light;

void main()
{
    // ambient
    vec3 ambient = light.ambient * material.ambient;
  	
    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * (diff * material.diffuse);
    
    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * (spec * material.specular);  
        
    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
} 

创建灯光的顶点着色器和片元着色器

light_mrt_cube.vs

#version 330 core
layout (location = 0) in vec3 aPos;

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

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

light_mrt_cube.fs

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

在这里插入图片描述

完整源码

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader_m.h
#include <shader_s.h>
// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/camera.h
#include <camera.h>

void InitGLFW();
bool CreateWindow();
bool InitGLAD();

// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn);
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);

// settings 窗口宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// 相机
Camera camera(glm::vec3(0.0f, 0.0f, 6.0f));
float lastX = static_cast<float>(SCR_WIDTH) / 2.0;
float lastY = static_cast<float>(SCR_HEIGHT) / 2.0;
bool firstMouse = true;

// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;

// lighting
glm::vec3 lightPos(0.5f, 0.5f, 1.0f);

GLFWwindow *window;

int main()
{
    InitGLFW(); // 初始化GLFW

    bool isCreated = CreateWindow(); // 创建一个窗口对象
    if (!isCreated)
        return -1;
    bool isGLAD = InitGLAD(); // 初始化GLAD,传入加载系统相关opengl函数指针的函数
    if (!isGLAD)
        return -1;

    // 启用深度测试
    glEnable(GL_DEPTH_TEST);

    // 构建和编译着色程序
    Shader lightingShader("shader/P1_Basic/08_LightModel/materials.vs", "shader/P1_Basic/08_LightModel/materials.fs");
    Shader lightCubeShader("shader/P1_Basic/08_LightModel/light_mrt_cube.vs", "shader/P1_Basic/08_LightModel/light_mrt_cube.fs");

    // 设置顶点数据(和缓冲区)并配置顶点属性
    // 1.设置立方体顶点输入  一共需要36个顶点
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.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, -1.0f,
         0.5f,  0.5f, -0.5f,  0.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, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.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,  1.0f,
         0.5f,  0.5f,  0.5f,  0.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,  1.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,  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,  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,  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,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.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, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.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, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.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,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.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,  1.0f,  0.0f
    };
    // 2.设置索引缓冲对象
    unsigned int VBO, cubeVAO;
    glGenVertexArrays(1, &cubeVAO);
    glGenBuffers(1, &VBO);

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

    glBindVertexArray(cubeVAO);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // normal attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);


    // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube)
    unsigned int lightCubeVAO;
    glGenVertexArrays(1, &lightCubeVAO);
    glBindVertexArray(lightCubeVAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // note that we update the lamp's position attribute's stride to reflect the updated buffer data
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 循环渲染
    while (!glfwWindowShouldClose(window))
    {

        // 计算帧间隔时间
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // 输入
        processInput(window);

        // 渲染
        // 清除颜色缓冲
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        // 清除深度缓冲
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // be sure to activate shader when setting uniforms/drawing objects
        lightingShader.use();
        lightingShader.setVec3("light.position", lightPos);
        lightingShader.setVec3("viewPos", camera.Position);

        // light properties
        glm::vec3 lightColor;
        lightColor.x = static_cast<float>(sin(glfwGetTime() * 2.0));
        lightColor.y = static_cast<float>(sin(glfwGetTime() * 0.7));
        lightColor.z = static_cast<float>(sin(glfwGetTime() * 1.3));
        glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);   // decrease the influence
        glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); // low influence
        lightingShader.setVec3("light.ambient", ambientColor);
        lightingShader.setVec3("light.diffuse", diffuseColor);
        lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

        // material properties
        lightingShader.setVec3("material.ambient", 1.0f, 0.5f, 0.31f);
        lightingShader.setVec3("material.diffuse", 1.0f, 0.5f, 0.31f);
        lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f); // specular lighting doesn't have full effect on this object's material
        lightingShader.setFloat("material.shininess", 32.0f);

        // view/projection transformations
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        lightingShader.setMat4("projection", projection);
        lightingShader.setMat4("view", view);

        // world transformation
        glm::mat4 model = glm::mat4(1.2f);
        lightingShader.setMat4("model", model);

        // render the cube
        glBindVertexArray(cubeVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        // also draw the lamp object
        lightCubeShader.use();
        lightCubeShader.setMat4("projection", projection);
        lightCubeShader.setMat4("view", view);
        model = glm::mat4(0.6f);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.5f)); // a smaller cube
        lightCubeShader.setMat4("model", model);

        glBindVertexArray(lightCubeVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        // 检查并调用事件,交换缓冲
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // 可选:一旦资源超出其用途,就取消分配所有资源:
    glDeleteVertexArrays(1, &cubeVAO);
    glDeleteVertexArrays(1, &lightCubeVAO);
    glDeleteBuffers(1, &VBO);
    // 释放/删除之前的分配的所有资源
    glfwTerminate();
    return 0;
}

void InitGLFW()
{
    // 初始化GLFW
    glfwInit();
    // 配置GLFW  第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择;
    // 第二个参数接受一个整型,用来设置这个选项的值。
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
}
bool CreateWindow()
{
    // 创建一个窗口对象
    window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        // 创建失败,终止程序
        glfwTerminate();
        return false;
    }
    // 将我们窗口的上下文设置为当前线程的主上下文
    glfwMakeContextCurrent(window);
    // 设置窗口大小改变时的回调函数
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    // 设置鼠标移动回调函数
    glfwSetCursorPosCallback(window, mouse_callback);
    // 设置滚轮滚动回调函数
    glfwSetScrollCallback(window, scroll_callback);

    // 隐藏并捕捉鼠标
    // glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    return true;
}
bool InitGLAD()
{
    // 初始化GLAD,传入加载系统相关opengl函数指针的函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        // 初始化失败,终止程序
        return false;
    }
    return true;
}

// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    // 设置窗口的维度
    glViewport(0, 0, width, height);
}

// 输入
void processInput(GLFWwindow *window)
{
    // 当用户按下esc键,我们设置window窗口的windowShouldClose属性为true
    // 关闭应用程序
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

// 鼠标移动回调函数
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn)
{
    // 长按T键,鼠标才能控制相机
    if (glfwGetKey(window, GLFW_KEY_1) != GLFW_PRESS)
        return;

    float xpos = static_cast<float>(xposIn);
    float ypos = static_cast<float>(yposIn);

    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

// 鼠标滚轮滚动回调函数
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(static_cast<float>(yoffset));
}

运行效果:
请添加图片描述

光照贴图

纹理与贴图关系

事实上,纹理与贴图原理是一样的,贴图也叫纹理贴图,其实都是使用一张覆盖物体的图像,让我们能够逐片段索引其独立的颜色值。

漫反射贴图

只是在光照场景中,它通常叫做一个漫反射贴图(Diffuse Map)(3D艺术家通常都这么叫它),它是一个表现了物体所有的漫反射颜色的纹理图像。

在着色器中使用漫反射贴图的方法和纹理教程中是完全一样的。

但这次我们会将纹理储存为Material结构体中的一个sampler2D。我们将之前定义的vec3漫反射颜色向量替换为漫反射贴图:

struct Material {
    sampler2D diffuse;
    vec3      specular;
    float     shininess;
}; 
...
in vec2 TexCoords;

注意sampler2D是所谓的不透明类型(Opaque Type),也就是说我们不能将它实例化,只能通过uniform来定义它。如果我们使用除uniform以外的方法(比如函数的参数)实例化这个结构体,GLSL会抛出一些奇怪的错误。这同样也适用于任何封装了不透明类型的结构体。

然后,我们将在片段着色器中再次需要纹理坐标,所以我们声明一个额外的输入变量。接下来我们只需要从纹理中采样片段的漫反射颜色值即可:

vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

将环境光的材质颜色设置为漫反射材质颜色同样的值:

vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

脚本实现

创建顶点着色器和片元着色器

lighting_maps.vs

#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 * vec4(FragPos, 1.0);
}

lighting_maps.fs

#version 330 core
out vec4 FragColor;

struct Material {
    sampler2D diffuse;
    vec3 specular;    
    float shininess;
}; 

struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

in vec3 FragPos;  
in vec3 Normal;  
in vec2 TexCoords;
  
uniform vec3 viewPos;
uniform Material material;
uniform Light light;

void main()
{
    // ambient
    vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
  	
    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  
    
    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * (spec * material.specular);  
        
    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
} 

完整源码

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader_m.h
#include <shader_s.h>
// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/camera.h
#include <camera.h>
#include <iostream>
void InitGLFW();
bool CreateWindow();
bool InitGLAD();

// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn);
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
unsigned int loadTexture(const char *path);

// settings 窗口宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// 相机
Camera camera(glm::vec3(0.0f, 0.0f, 6.0f));
float lastX = static_cast<float>(SCR_WIDTH) / 2.0;
float lastY = static_cast<float>(SCR_HEIGHT) / 2.0;
bool firstMouse = true;

// timing
float deltaTime = 0.0f; // time between current frame and last frame
float lastFrame = 0.0f;

// lighting
glm::vec3 lightPos(0.5f, 0.5f, 1.0f);

GLFWwindow *window;

int main()
{
    InitGLFW(); // 初始化GLFW

    bool isCreated = CreateWindow(); // 创建一个窗口对象
    if (!isCreated)
        return -1;
    bool isGLAD = InitGLAD(); // 初始化GLAD,传入加载系统相关opengl函数指针的函数
    if (!isGLAD)
        return -1;

    // 启用深度测试
    glEnable(GL_DEPTH_TEST);

    // 构建和编译着色程序
    Shader lightingShader("shader/P1_Basic/08_LightModel/lighting_maps.vs", "shader/P1_Basic/08_LightModel/lighting_maps.fs");
    Shader lightCubeShader("shader/P1_Basic/08_LightModel/light_mrt_cube.vs", "shader/P1_Basic/08_LightModel/light_mrt_cube.fs");

    // 设置顶点数据(和缓冲区)并配置顶点属性
    // 1.设置立方体顶点输入  一共需要36个顶点
    float vertices[] = {
        // positions          // normals           // texture coords
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
        -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,

        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
        -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,

        -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,

        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
        0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,

        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f};
    // 2.设置索引缓冲对象
    unsigned int VBO, cubeVAO;
    glGenVertexArrays(1, &cubeVAO);
    glGenBuffers(1, &VBO);

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

    glBindVertexArray(cubeVAO);
    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);

    // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube)
    unsigned int lightCubeVAO;
    glGenVertexArrays(1, &lightCubeVAO);
    glBindVertexArray(lightCubeVAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // note that we update the lamp's position attribute's stride to reflect the updated buffer data
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);
    glEnableVertexAttribArray(0);

    // 加载纹理
    https://learnopengl-cn.github.io/img/02/04/container2.png
    unsigned int diffuseMap = loadTexture("image/04_Textures/container2.png");

    // shader configuration
    // --------------------
    lightingShader.use();
    lightingShader.setInt("material.diffuse", 0);

    // 循环渲染
    while (!glfwWindowShouldClose(window))
    {
        // 计算帧间隔时间
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // 输入
        processInput(window);

        // 渲染
        // 清除颜色缓冲
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        // 清除深度缓冲
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // be sure to activate shader when setting uniforms/drawing objects
        lightingShader.use();
        lightingShader.setVec3("light.position", lightPos);
        lightingShader.setVec3("viewPos", camera.Position);

        // light properties
        lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
        lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f);
        lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

        // material properties
        lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
        lightingShader.setFloat("material.shininess", 64.0f);

        // view/projection transformations
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        lightingShader.setMat4("projection", projection);
        lightingShader.setMat4("view", view);

        // world transformation
        glm::mat4 model = glm::mat4(1.0f);
        lightingShader.setMat4("model", model);
		// bind diffuse map
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, diffuseMap);

        // render the cube
        glBindVertexArray(cubeVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        // also draw the lamp object
        lightCubeShader.use();
        lightCubeShader.setMat4("projection", projection);
        lightCubeShader.setMat4("view", view);
        model = glm::mat4(1.0f);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.4f)); // a smaller cube
        lightCubeShader.setMat4("model", model);

        glBindVertexArray(lightCubeVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        // 检查并调用事件,交换缓冲
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // 可选:一旦资源超出其用途,就取消分配所有资源:
    glDeleteVertexArrays(1, &cubeVAO);
    glDeleteVertexArrays(1, &lightCubeVAO);
    glDeleteBuffers(1, &VBO);
    // 释放/删除之前的分配的所有资源
    glfwTerminate();
    return 0;
}

void InitGLFW()
{
    // 初始化GLFW
    glfwInit();
    // 配置GLFW  第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择;
    // 第二个参数接受一个整型,用来设置这个选项的值。
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
}
bool CreateWindow()
{
    // 创建一个窗口对象
    window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        // 创建失败,终止程序
        glfwTerminate();
        return false;
    }
    // 将我们窗口的上下文设置为当前线程的主上下文
    glfwMakeContextCurrent(window);
    // 设置窗口大小改变时的回调函数
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    // 设置鼠标移动回调函数
    glfwSetCursorPosCallback(window, mouse_callback);
    // 设置滚轮滚动回调函数
    glfwSetScrollCallback(window, scroll_callback);

    // 隐藏并捕捉鼠标
    // glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    return true;
}
bool InitGLAD()
{
    // 初始化GLAD,传入加载系统相关opengl函数指针的函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        // 初始化失败,终止程序
        return false;
    }
    return true;
}

// 窗口大小改变时调用
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    // 设置窗口的维度
    glViewport(0, 0, width, height);
}

// 输入
void processInput(GLFWwindow *window)
{
    // 当用户按下esc键,我们设置window窗口的windowShouldClose属性为true
    // 关闭应用程序
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

// 鼠标移动回调函数
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn)
{
    // 长按T键,鼠标才能控制相机
    if (glfwGetKey(window, GLFW_KEY_1) != GLFW_PRESS)
        return;

    float xpos = static_cast<float>(xposIn);
    float ypos = static_cast<float>(yposIn);

    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}

// 鼠标滚轮滚动回调函数
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(static_cast<float>(yoffset));
}
unsigned int loadTexture(char const *path)
{
    unsigned int textureID;
    glGenTextures(1, &textureID);

    int width, height, nrComponents;
    unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
    if (data)
    {
        GLenum format;
        if (nrComponents == 1)
            format = GL_RED;
        else if (nrComponents == 3)
            format = GL_RGB;
        else if (nrComponents == 4)
            format = GL_RGBA;

        glBindTexture(GL_TEXTURE_2D, textureID);
        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);

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

        stbi_image_free(data);
    }
    else
    {
        std::cout << "Texture failed to load at path: " << path << std::endl;
        stbi_image_free(data);
    }

    return textureID;
}

运行效果 :

在这里插入图片描述

高光反射贴图

我们同样可以使用一个专门用于镜面高光的纹理贴图。这也就意味着我们需要生成一个黑白的(如果你想得话也可以是彩色的)纹理,来定义物体每部分的镜面光强度。

镜面高光的强度可以通过图像每个像素的亮度来获取。镜面光贴图上的每个像素都可以由一个颜色向量来表示,比如说黑色代表颜色向量vec3(0.0),灰色代表颜色向量vec3(0.5)。

在片段着色器中,我们接下来会取样对应的颜色值并将它乘以光源的镜面强度。一个像素越「白」,乘积就会越大,物体的镜面光分量就会越亮。

从实际角度来说,木头其实也有镜面高光,尽管它的反光度(Shininess)很小(更多的光被散射),影响也比较小,但是为了教学目的,我们可以假设木头不会对镜面光有任何反应。

采样镜面光贴图

创建片段着色器的材质属性,让其接受一个sampler2D而不是vec3作为镜面光分量:

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
};

然后,通过采样镜面光贴图,来获取片段所对应的镜面光强度:

vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));  
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
FragColor = vec4(ambient + diffuse + specular, 1.0);

脚本实现

我们只创建片元着色器,顶点着色器和上面漫反射一样的,源码也类似

源码

 ...
 // 加载纹理
    // https://learnopengl-cn.github.io/img/02/04/container2.png
    unsigned int diffuseMap = loadTexture("image/04_Textures/container2.png");
    // https://learnopengl-cn.github.io/img/02/04/container2_specular.png
    unsigned int specularMap = loadTexture("image/04_Textures/container2_specular.png");
    ...

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

 ...
         // bind diffuse map
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, diffuseMap);
        // bind specular map
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, specularMap);
 ...
}

lighting_specular_maps.fs

#version 330 core
out vec4 FragColor;

struct Material {
    sampler2D diffuse;
    sampler2D specular;    
    float shininess;
}; 

struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

in vec3 FragPos;  
in vec3 Normal;  
in vec2 TexCoords;
  
uniform vec3 viewPos;
uniform Material material;
uniform Light light;

void main()
{
    // ambient
    vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
  	
    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  
    
    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;  
        
    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
} 

运行效果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1434768.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式1-访问者模式

访问者模式是一种行为设计模式&#xff0c;它允许你定义在对象结构中的元素上进行操作的新操作&#xff0c;而无需修改这些元素的类。这种模式的主要思想是将算法与元素的结构分离开&#xff0c;使得可以在不修改元素结构的情况下定义新的操作。 所谓算法与元素结构分离&#x…

不会PS怎么抠图?分享几个电商抠图的方法

在工作中&#xff0c;物品抠图是一项常见的任务。为了更好地展示物品&#xff0c;需要将其从背景中抠出来&#xff0c;以便与其他元素进行组合或展示。但是&#xff0c;手动抠图不仅费时费力&#xff0c;而且效果往往不尽如人意。这时&#xff0c;一款强大的物品抠图软件就成为…

【数据结构与算法】(10)基础数据结构 之 堆 建堆及堆排序 详细代码示例讲解

目录 2.9 堆建堆习题E01. 堆排序E02. 数组中第K大元素-Leetcode 215E03. 数据流中第K大元素-Leetcode 703E04. 数据流的中位数-Leetcode 295 2.9 堆 以大顶堆为例&#xff0c;相对于之前的优先级队列&#xff0c;增加了堆化等方法 public class MaxHeap {int[] array;int siz…

【已解决】Oracle 12541 TNS 无监听程序

目录 1、找到Oracle监听服务&#xff08;OracleOraDb10g_homeTNLListener&#xff09;&#xff0c;停止运行 2、首先查看监听文件是否超过4G 3、修改配置文件 连接oracle突然报错&#xff0c;提示Oracle 12541 TNS 无监听程序&#xff0c;可以按照以下步骤解决 1、找到Ora…

【前沿技术杂谈:深度学习新纪元】探索人工智能领域的革命性进展

【前沿技术杂谈&#xff1a;深度学习新纪元】探索人工智能领域的革命性进展 深度学习的进展深度学习的基本原理和算法深度学习的历史发展神经网络的基本构成神经元层次结构激活函数 关键技术和算法反向传播算法卷积神经网络&#xff08;CNN&#xff09;循环神经网络&#xff08…

【操作系统·考研】I/O管理概述

1.I/O设备 1.1 块设备 信息交换以数据块为单位&#xff0c;它属于有结构设备。 块设备传输速率较高&#xff0c;可寻址&#xff0c;且可对该设备随机地的读写。 栗子&#x1f330;&#xff1a;磁盘。 1.2 字符设备 信息交换以字符为单位&#xff0c;属于无结构类型。 字符…

扩展鸿蒙textinput组件

扩展鸿蒙textinput组件&#xff0c;支持快速扩展展性&#xff0c;标题文本等&#xff0c;文本内容双向绑定、文本组件快速复用。 组件代码 /*** 单选文本*/ Component export default struct DiygwInput{//绑定的值Link value:string;//未选中图标State labelImg: Resource …

《热辣滚烫》预售狂潮来袭,贾玲、马丽、杨紫三大女神联袂出演。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 《热辣滚烫》预售票房一日破1300万&#xff0c;燃爆春节档&am…

自定义Dockerfile构建运行springboot

自定义Dockerfile构建运行springboot 通过dockerfile生成自定义nginx镜像 &#xff01;&#xff01;&#xff01;docker 必须在linux环境下才能进行如果你是window则需要装虚拟机 新建一个文件名字为Dockerfile&#xff0c;无需后缀 文件完整名就是Dockerfile,也可以自定义d…

有向图的拓扑排序-BFS求解

题目 给定一个n个点m条边的有向图&#xff0c;图中可能存在重边和自环。 请输出任意一个该有向图的拓扑序列&#xff0c;如果拓扑序列不存在&#xff0c;则输出-1。 若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y)&#xff0c;x在A中都出现在y之前&#xff0c;则称…

linux中的makefile

(码字不易&#xff0c;关注一下吧w~~w&#xff09; makefile文件是用来管理项目文件&#xff0c;通过执行make命令&#xff0c;make就会解析并执行makefile文件。 命名&#xff1a;makefile或者Makefile 规则&#xff1a; 目标文件&#xff1a;依赖文件 &#xff08;tab)命…

Narrative Visualization: Telling Stories with Data

作者&#xff1a;Edward Segel、Jeffrey Heer 发表&#xff1a;TVCG&#xff0c; 机构&#xff1a;UW Interactive Data Lab 【原斯坦福可视化组】 1.概述 静态可视化&#xff1a;在一大串的文本描述中&#xff0c;可视化作为提供证据和细节的图表出现新兴可视化&#xff1a…

设计模式学习笔记(一):基本概念;UML

文章目录 参考面向对象的设计原则创建型模式结构型模式行为型模式 UML视图图&#xff08;Diagram&#xff09;模型元素(Model Element)通用机制类之间的关系关联关系复杂&#xff01;&#xff01;聚合关系组合关系 依赖关系泛化关系接口与实现关系 参考 https://github.com/fa…

ubuntu开机报错/dev/nume0n1p2:clean

本来是开机卡在这个界面&#xff0c;经过以下操作&#xff0c;变成这种了 现在的问题变成linux卡在 failed to start NVIDIA Persistence Daemon 按照下面的操作方法&#xff0c;可以有开机界面了。但是输入密码后&#xff0c;一直在登录界面 1.方式一&#xff1a;重新安装显…

回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测(SE注意力机制)

回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&#xff09; 目录 回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&…

Python爬虫实战 | 京东平台电商API接口采集京东商品京东工业商品详情数据

item_get-获得JD商品详情API测试 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cac…

C++多线程学习[六]: 多线程之间的同步

一、同步问题 实际开发场景中有很多需要同步的情况&#xff0c;例如&#xff0c;音频和视频的同步输出、或者通讯能够第一时间同步接受处理… 二、多线程同步demo 可以看到cond可以阻塞等待&#xff08;wait&#xff09;可以通知一个线程(notify_one)也可以通知所有的线程&am…

SQL Server数据库日志查看若已满需要清理的三种解决方案

首先查看获取实例中每个数据库日志文件大小及使用情况&#xff0c;根据数据库日志占用百分比来清理 DBCC SQLPERF(LOGSPACE) 第一种解决方案&#xff1a; 在数据库上点击右键 → 选择 属性 → 选择 文件&#xff0c;然后增加数据库日志文件的文件大小。 第二种解决方案 手动…

如何使用VS Code编写小游戏并实现公网游玩本地游戏【内网穿透】

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程&#xff0c;我们将通过VS Code实现远程开发MENJA小游戏&#xff0c;并通过cpolar内网穿透发布到公网&#xff0c;分…

细说开源软件的影响力分析

开源软件的影响力分析 一、开源软件如何推动技术创新 开源软件以其开放源代码的特性&#xff0c;极大地推动了全球软件技术的创新和发展。这种开放性不仅使得开发者能够自由地查看、修改和使用源代码&#xff0c;还促进了全球开发者之间的深度协作和交流。 1.1 促进全球协作&…