屏幕空间反射SSR-笔记

news2025/7/16 17:38:23

屏幕空间反射SSR
相关文章:
[OpenGL] 屏幕空间反射效果

Games202-RealTime GI in Screen Space

github上的例子,使用visual studio2019
github例子对应的文章


在这里插入图片描述


使用OpenGL和C++实现发光柱子的SSR倒影

下面是一个使用OpenGL和C++实现屏幕空间反射(SSR)来创建发光柱子倒影的完整示例。这个实现包括基本的SSR技术、发光效果和倒影渲染。

1. 基本框架

首先,我们需要设置OpenGL环境并创建必要的着色器。

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <vector>
#include "shader.h" // 自定义着色器加载类

// 窗口尺寸
const unsigned int SCR_WIDTH = 1280;
const unsigned int SCR_HEIGHT = 720;

// 全局变量
GLuint gBuffer, gPosition, gNormal, gAlbedoSpec;
GLuint quadVAO, quadVBO;
GLuint cubeVAO, cubeVBO;
GLuint floorVAO, floorVBO;

// 函数声明
void renderScene(Shader &shader);
void renderQuad();
void renderCube();
void setupGBuffer();
void setupScreenQuad();
void setupGeometry();

2. 初始化GBuffer

SSR需要几何缓冲区(GBuffer)来存储位置、法线和材质信息。

void setupGBuffer() {
    glGenFramebuffers(1, &gBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
    
    // 位置颜色缓冲
    glGenTextures(1, &gPosition);
    glBindTexture(GL_TEXTURE_2D, gPosition);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0);
    
    // 法线颜色缓冲
    glGenTextures(1, &gNormal);
    glBindTexture(GL_TEXTURE_2D, gNormal);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0);
    
    // 颜色 + 镜面颜色缓冲
    glGenTextures(1, &gAlbedoSpec);
    glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedoSpec, 0);
    
    // 告诉OpenGL我们要使用哪些颜色附件进行渲染
    GLuint attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
    glDrawBuffers(3, attachments);
    
    // 创建并附加深度缓冲
    GLuint rboDepth;
    glGenRenderbuffers(1, &rboDepth);
    glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth);
    
    // 检查完整性
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "Framebuffer not complete!" << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

3. 几何体设置

创建柱子和地板的几何体。

void setupGeometry() {
    // 柱子 (圆柱体)
    std::vector<float> vertices;
    const float radius = 0.5f;
    const float height = 2.0f;
    const int segments = 32;
    
    // 侧面
    for (int i = 0; i <= segments; ++i) {
        float angle = 2.0f * M_PI * float(i) / float(segments);
        float x = radius * cosf(angle);
        float z = radius * sinf(angle);
        
        // 底部顶点
        vertices.push_back(x);
        vertices.push_back(0.0f);
        vertices.push_back(z);
        vertices.push_back(x);
        vertices.push_back(0.0f);
        vertices.push_back(z);
        vertices.push_back(0.0f);
        vertices.push_back(1.0f);
        vertices.push_back(0.0f);
        
        // 顶部顶点
        vertices.push_back(x);
        vertices.push_back(height);
        vertices.push_back(z);
        vertices.push_back(x);
        vertices.push_back(0.0f);
        vertices.push_back(z);
        vertices.push_back(0.0f);
        vertices.push_back(1.0f);
        vertices.push_back(0.0f);
    }
    
    glGenVertexArrays(1, &cubeVAO);
    glGenBuffers(1, &cubeVBO);
    glBindVertexArray(cubeVAO);
    glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), &vertices[0], GL_STATIC_DRAW);
    
    // 位置属性
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)0);
    // 法线属性
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)(3 * sizeof(float)));
    // 切线属性
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)(6 * sizeof(float)));
    
    // 地板 (平面)
    float floorVertices[] = {
        // 位置             // 法线         // 切线
        -5.0f, 0.0f, -5.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
         5.0f, 0.0f, -5.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
         5.0f, 0.0f,  5.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
        -5.0f, 0.0f,  5.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f
    };
    unsigned int floorIndices[] = {
        0, 1, 2,
        0, 2, 3
    };
    
    glGenVertexArrays(1, &floorVAO);
    glGenBuffers(1, &floorVBO);
    GLuint floorEBO;
    glGenBuffers(1, &floorEBO);
    
    glBindVertexArray(floorVAO);
    glBindBuffer(GL_ARRAY_BUFFER, floorVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(floorVertices), floorVertices, GL_STATIC_DRAW);
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, floorEBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(floorIndices), floorIndices, GL_STATIC_DRAW);
    
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)(6 * sizeof(float)));
    
    glBindVertexArray(0);
}

4. 着色器

我们需要几个着色器:几何通道着色器、光照着色器和SSR着色器。

几何通道着色器 (geometry.vs 和 geometry.fs)

// geometry.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec3 aTangent;

out VS_OUT {
    vec3 FragPos;
    vec3 Normal;
    vec3 Tangent;
} vs_out;

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

void main() {
    vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
    vs_out.Normal = mat3(transpose(inverse(model))) * aNormal;
    vs_out.Tangent = mat3(model) * aTangent;
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
// geometry.fs
#version 330 core
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;

in VS_OUT {
    vec3 FragPos;
    vec3 Normal;
    vec3 Tangent;
} fs_in;

uniform vec3 color;
uniform float specular;
uniform float emissive;

void main() {
    // 存储片段位置向量到G缓冲
    gPosition = fs_in.FragPos;
    // 存储法线向量到G缓冲
    gNormal = normalize(fs_in.Normal);
    // 存储漫反射颜色
    gAlbedoSpec.rgb = color;
    // 存储镜面强度
    gAlbedoSpec.a = specular;
    
    // 如果是发光物体,增加自发光
    if (emissive > 0.0) {
        gAlbedoSpec.rgb += color * emissive;
    }
}

SSR着色器 (ssr.vs 和 ssr.fs)

// ssr.vs
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aTexCoords;

out vec2 TexCoords;

void main() {
    TexCoords = aTexCoords;
    gl_Position = vec4(aPos, 0.0, 1.0);
}
// ssr.fs
#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
uniform sampler2D sceneTexture;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 invView;
uniform mat4 invProjection;

uniform vec3 cameraPos;

const float maxRayDistance = 100.0;
const int maxSteps = 128;
const float stride = 1.0;
const float binarySearchSteps = 8;
const float rayHitThreshold = 0.01;

vec3 binarySearch(inout vec3 dir, inout vec3 hitCoord, inout float dDepth);
vec4 rayCast(vec3 dir, inout vec3 hitCoord, out bool success);
vec3 hash(vec3 a);

void main() {
    // 获取输入数据
    vec3 FragPos = texture(gPosition, TexCoords).rgb;
    vec3 Normal = texture(gNormal, TexCoords).rgb;
    vec3 Albedo = texture(gAlbedoSpec, TexCoords).rgb;
    float Specular = texture(gAlbedoSpec, TexCoords).a;
    
    // 计算反射向量
    vec3 viewDir = normalize(FragPos - cameraPos);
    vec3 reflectDir = reflect(viewDir, Normal);
    
    // 初始化射线
    vec3 hitPos = FragPos;
    bool success = false;
    vec4 coords = rayCast(reflectDir, hitPos, success);
    
    vec3 SSR = vec3(0.0);
    if (success) {
        SSR = texture(sceneTexture, coords.xy).rgb;
        // 根据距离衰减
        float fade = 1.0 - smoothstep(0.0, 1.0, length(FragPos - hitPos) / maxRayDistance);
        SSR *= fade * Specular;
    }
    
    // 混合原始颜色和SSR
    FragColor = vec4(SSR + Albedo, 1.0);
}

vec3 binarySearch(inout vec3 dir, inout vec3 hitCoord, inout float dDepth) {
    float depth;
    vec4 projectedCoord;
    
    for (int i = 0; i < binarySearchSteps; i++) {
        projectedCoord = projection * view * vec4(hitCoord, 1.0);
        projectedCoord.xy /= projectedCoord.w;
        projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
        
        depth = texture(gPosition, projectedCoord.xy).z;
        dDepth = hitCoord.z - depth;
        
        dir *= 0.5;
        if (dDepth > 0.0)
            hitCoord += dir;
        else
            hitCoord -= dir;
    }
    
    projectedCoord = projection * view * vec4(hitCoord, 1.0);
    projectedCoord.xy /= projectedCoord.w;
    projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
    
    return vec3(projectedCoord.xy, depth);
}

vec4 rayCast(vec3 dir, inout vec3 hitCoord, out bool success) {
    dir *= stride;
    
    float depth;
    int steps;
    vec4 projectedCoord;
    float dDepth;
    
    for (steps = 0; steps < maxSteps; steps++) {
        hitCoord += dir;
        
        projectedCoord = projection * view * vec4(hitCoord, 1.0);
        projectedCoord.xy /= projectedCoord.w;
        projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
        
        // 检查是否在屏幕外
        if (projectedCoord.x < 0.0 || projectedCoord.x > 1.0 || 
            projectedCoord.y < 0.0 || projectedCoord.y > 1.0) {
            success = false;
            return vec4(0.0);
        }
        
        depth = texture(gPosition, projectedCoord.xy).z;
        dDepth = hitCoord.z - depth;
        
        if (dir.z - dDepth < 1.2) {
            if (dDepth <= 0.0) {
                vec3 Result = binarySearch(dir, hitCoord, dDepth);
                
                if (dDepth < rayHitThreshold && Result.z - hitCoord.z < rayHitThreshold) {
                    success = true;
                    return vec4(Result, 1.0);
                }
                break;
            }
        }
    }
    
    success = false;
    return vec4(0.0);
}

vec3 hash(vec3 a) {
    a = fract(a * 0.8);
    a += dot(a, a.xyz + 19.19);
    return fract((a.xxy + a.yxx) * a.zyx);
}

5. 渲染循环

int main() {
    // 初始化GLFW和OpenGL
    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, "SSR Glowing Pillar", NULL, NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK) {
        std::cout << "Failed to initialize GLEW" << std::endl;
        return -1;
    }
    
    // 配置全局OpenGL状态
    glEnable(GL_DEPTH_TEST);
    
    // 构建和编译着色器
    Shader geometryShader("geometry.vs", "geometry.fs");
    Shader ssrShader("ssr.vs", "ssr.fs");

-----------------------------------------------------------------------
# 屏幕空间反射(SSR)着色器详细解释

`ssr.fs`(屏幕空间反射片段着色器)是实现屏幕空间反射效果的核心部分。下面我将详细解释这个着色器的每个关键部分。

## 1. 输入输出和统一变量

```glsl
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D gPosition;  // 位置纹理(GBuffer)
uniform sampler2D gNormal;    // 法线纹理(GBuffer)
uniform sampler2D gAlbedoSpec;// 颜色和镜面强度纹理(GBuffer)
uniform sampler2D sceneTexture; // 场景颜色纹理

uniform mat4 projection;      // 投影矩阵
uniform mat4 view;            // 视图矩阵
uniform mat4 invView;         // 视图逆矩阵
uniform mat4 invProjection;   // 投影逆矩阵

uniform vec3 cameraPos;       // 相机位置

这些变量提供了SSR计算所需的所有输入数据:

  • gPosition, gNormal, gAlbedoSpec来自几何缓冲区(GBuffer)
  • sceneTexture是已经渲染的场景颜色
  • 矩阵用于空间转换
  • cameraPos用于计算视线方向

2. 常量定义

const float maxRayDistance = 100.0;  // 最大射线距离
const int maxSteps = 128;            // 最大步进次数
const float stride = 1.0;            // 射线步进步长
const float binarySearchSteps = 8;   // 二分查找步数
const float rayHitThreshold = 0.01;  // 射线命中阈值

这些常量控制SSR算法的质量和性能:

  • maxRayDistance限制反射射线的最大长度
  • maxSteps限制射线步进的最大次数
  • stride控制每一步的步长
  • binarySearchSteps控制精确命中时的二分查找精度
  • rayHitThreshold定义何时认为射线命中表面

3. 主函数(main)

void main() {
    // 获取输入数据
    vec3 FragPos = texture(gPosition, TexCoords).rgb;
    vec3 Normal = texture(gNormal, TexCoords).rgb;
    vec3 Albedo = texture(gAlbedoSpec, TexCoords).rgb;
    float Specular = texture(gAlbedoSpec, TexCoords).a;
    
    // 计算反射向量
    vec3 viewDir = normalize(FragPos - cameraPos);
    vec3 reflectDir = reflect(viewDir, Normal);
    
    // 初始化射线
    vec3 hitPos = FragPos;
    bool success = false;
    vec4 coords = rayCast(reflectDir, hitPos, success);
    
    vec3 SSR = vec3(0.0);
    if (success) {
        SSR = texture(sceneTexture, coords.xy).rgb;
        // 根据距离衰减
        float fade = 1.0 - smoothstep(0.0, 1.0, length(FragPos - hitPos) / maxRayDistance);
        SSR *= fade * Specular;
    }
    
    // 混合原始颜色和SSR
    FragColor = vec4(SSR + Albedo, 1.0);
}

主函数流程:

  1. 从GBuffer获取当前像素的位置、法线、颜色和镜面强度
  2. 计算从相机到当前像素的视线方向(viewDir)
  3. 使用GLSL内置的reflect函数计算反射方向
  4. 调用rayCast函数进行射线步进,寻找反射点
  5. 如果找到反射点,从场景纹理中采样反射颜色
  6. 根据距离和镜面强度调整反射强度
  7. 将反射颜色与原始颜色混合输出

4. 射线步进(rayCast)

vec4 rayCast(vec3 dir, inout vec3 hitCoord, out bool success) {
    dir *= stride;
    
    float depth;
    int steps;
    vec4 projectedCoord;
    float dDepth;
    
    for (steps = 0; steps < maxSteps; steps++) {
        hitCoord += dir;
        
        projectedCoord = projection * view * vec4(hitCoord, 1.0);
        projectedCoord.xy /= projectedCoord.w;
        projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
        
        // 检查是否在屏幕外
        if (projectedCoord.x < 0.0 || projectedCoord.x > 1.0 || 
            projectedCoord.y < 0.0 || projectedCoord.y > 1.0) {
            success = false;
            return vec4(0.0);
        }
        
        depth = texture(gPosition, projectedCoord.xy).z;
        dDepth = hitCoord.z - depth;
        
        if (dir.z - dDepth < 1.2) {
            if (dDepth <= 0.0) {
                vec3 Result = binarySearch(dir, hitCoord, dDepth);
                
                if (dDepth < rayHitThreshold && Result.z - hitCoord.z < rayHitThreshold) {
                    success = true;
                    return vec4(Result, 1.0);
                }
                break;
            }
        }
    }
    
    success = false;
    return vec4(0.0);
}

射线步进流程:

  1. 沿着反射方向(dir)逐步前进
  2. 每一步将世界坐标转换为屏幕坐标(projectedCoord)
  3. 检查是否超出屏幕边界
  4. 从GBuffer获取当前位置的深度值
  5. 比较当前射线深度与场景深度(dDepth)
  6. 当射线进入表面下方时(dDepth <= 0.0),进入二分查找阶段
  7. 如果找到足够接近的命中点,返回命中坐标

5. 二分查找(binarySearch)

vec3 binarySearch(inout vec3 dir, inout vec3 hitCoord, inout float dDepth) {
    float depth;
    vec4 projectedCoord;
    
    for (int i = 0; i < binarySearchSteps; i++) {
        projectedCoord = projection * view * vec4(hitCoord, 1.0);
        projectedCoord.xy /= projectedCoord.w;
        projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
        
        depth = texture(gPosition, projectedCoord.xy).z;
        dDepth = hitCoord.z - depth;
        
        dir *= 0.5;
        if (dDepth > 0.0)
            hitCoord += dir;
        else
            hitCoord -= dir;
    }
    
    projectedCoord = projection * view * vec4(hitCoord, 1.0);
    projectedCoord.xy /= projectedCoord.w;
    projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
    
    return vec3(projectedCoord.xy, depth);
}

二分查找流程:

  1. 在粗略命中点附近进行精确查找
  2. 每一步将搜索方向减半(dir *= 0.5)
  3. 根据深度差调整搜索方向
  4. 最终返回精确的屏幕坐标和深度值

6. 关键算法细节

  1. 屏幕空间限制:SSR只能在屏幕可见的范围内工作,无法反射屏幕外的物体。

  2. 射线步进策略

    • 先用大步长快速遍历
    • 发现潜在命中点时切换到二分查找精确确定位置
  3. 深度比较:通过比较射线深度和场景深度确定是否命中表面。

  4. 衰减处理:反射强度随距离衰减,避免远处反射过于明显。

  5. 镜面控制:使用材质的镜面系数控制反射强度,只有高光材质才有强烈反射。

7. 优化考虑

  1. 早期终止:当射线超出屏幕或达到最大距离时提前终止。

  2. 自适应步长:可以根据场景复杂度动态调整步长。

  3. 模糊处理:可以对反射结果进行适当模糊以减少噪点。

  4. 边缘淡化:在屏幕边缘淡化反射效果,掩盖SSR的局限性。

这个SSR实现提供了实时反射效果,特别适合光滑表面和发光物体的倒影渲染,如问题中要求的发光柱子效果。

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

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

相关文章

动态规划算法深度解析:0-1背包问题(含完整流程)

简介&#xff1a; 0-1背包问题是经典的组合优化问题&#xff1a;给定一组物品&#xff08;每个物品有重量和价值&#xff09;&#xff0c;在背包容量限制下选择物品装入背包&#xff0c;要求总价值最大化且每个物品不可重复选取。 动态规划核心思想 通过构建二维状态表dp[i]…

LeetCode刷题SQL笔记

系列博客目录 文章目录 系列博客目录1.distinct关键字 去除重复2.char_length()3.group by 与 count()连用4.date类型有个函数datediff()5.mod 函数6.join和left join的区别1. **JOIN&#xff08;内连接&#xff0c;INNER JOIN&#xff09;**示例&#xff1a; 2. **LEFT JOIN&a…

循环神经网络 - 参数学习之随时间反向传播算法

本文中&#xff0c;我们以同步的序列到序列模式为例来介绍循环神经网络的参数学习。 循环神经网络中存在一个递归调用的函数 &#x1d453;(⋅)&#xff0c;因此其计算参数梯度的方式和前馈神经网络不太相同。在循环神经网络中主要有两种计算梯度的方式&#xff1a;随时间反向…

球类(继承和多态)

父类Ball&#xff0c;设置为抽象类&#xff0c;调用get和set方法创建对象&#xff0c;将子类重写的功能函数抽象化。 // 抽象球类 abstract class Ball {private String name;private double radius; // 半径private double weight; // 重量private double price; // 价格// 构…

DFS和BFS的模版

dfs dfs金典例题理解就是走迷宫 P1605 迷宫 - 洛谷 dfs本质上在套一个模版&#xff1a; ///dfs #include<bits/stdc.h> using namespace std; int a[10][10]{0}; int m,n,t,ans0; int ex,ey; int v[10][10]{0}; int dx[4]{-1,0,1,0}; int dy[4]{0,1,0,-1}; void dfs(in…

Ansible Playbook 进阶探秘:Handlers、变量、循环及条件判断全解析

192.168.60.100ansible.com192.168.60.110 client-1.com 192.168.60.120client-2.com192.168.60.130client-1.com 一、Handlers 介绍&#xff1a;在发生改变时执行的操作(类似puppet通知机制) 示例&#xff1a; 当apache的配置文件发生改变时&#xff0c;apache服务才会重启…

MATLAB的24脉波整流器Simulink仿真与故障诊断

本博客来源于CSDN机器鱼&#xff0c;未同意任何人转载。 更多内容&#xff0c;欢迎点击本专栏目录&#xff0c;查看更多内容。 目录 0 引言 1 故障数据采集 2 故障特征提取 3 故障诊断分类 4 结语 本博客内容是在MATLAB2023下完成。 0 引言 对于电力电子电路的故障诊断…

linux第三次作业

1、将你的虚拟机的网卡模式设置为nat模式&#xff0c;给虚拟机网卡配置三个主机位分别为100、200、168的ip地址 2、测试你的虚拟机是否能够ping通网关和dns&#xff0c;如果不能请修改网关和dns的地址 3、将如下内容写入/etc/hosts文件中&#xff08;如果有多个ip地址则写多行&…

国标GB28181视频平台EasyCVR顺应智慧农业自动化趋势,打造大棚实时视频监控防线

一、方案背景 近年来&#xff0c;温室大棚种植技术凭借其显著的优势&#xff0c;在提升农作物产量和质量、丰富农产品供应方面发挥了重要的作用&#xff0c;极大改善了人们的生活水平&#xff0c;得到了广泛的推广和应用。大棚内的温度、湿度、光照度和二氧化碳浓度等环境因素…

HOOPS Visualize:跨平台、高性能的三维图形渲染技术解析

在当今数字化时代&#xff0c;三维可视化技术已成为众多行业的核心竞争力。HOOPS Visualize作为一款功能强大的三维图形渲染引擎&#xff0c;凭借其卓越的渲染能力、跨平台支持、丰富的交互功能、高度定制化以及快速部署等特性&#xff0c;为开发人员提供了构建高质量、高性能3…

蓝桥杯 C/C++ 组历届真题合集速刷(一)

一、1.单词分析 - 蓝桥云课 &#xff08;模拟、枚举&#xff09;算法代码&#xff1a; #include <bits/stdc.h> using namespace std;int main() {string s;cin>>s;unordered_map<char,int> mp;for(auto ch:s){mp[ch];}char result_charz;int max_count0;fo…

多类型医疗自助终端智能化升级路径(代码版.上)

大型医疗自助终端的智能化升级是医疗信息化发展的重要方向,其思维链一体化路径需要围绕技术架构、数据流协同、算法优化和用户体验展开: 一、技术架构层:分布式边缘计算与云端协同 以下针对技术架构层的分布式边缘计算与云端协同模块,提供具体编程实现方案: 一、边缘节点…

区间 DP 详解

文章目录 区间 DP分割型合并型环形合并 区间 DP 区间 DP&#xff0c;就是在对一段区间进行了若干次操作后的最小代价&#xff0c;一般是合并和拆分类型。 分割型 分割型&#xff0c;指把一个区间内的几项分开拆成一份一份的&#xff0c;再全部合起来就是当前答案&#xff0c…

QAM 信号的距离以及能量归一化

QAM星座图平均功率能量_星座图功率计算-CSDN博客 正交幅度调制(QAM) - Vinson88 - 博客园 不同阶QAM调制星座图中&#xff0c;符号能量的归一化计算原理_qpsk的星座图归一化-CSDN博客 https://zhuanlan.zhihu.com/p/690157236

Reactive编程框架与工具

文章目录 6.2 后端 Reactive 框架6.2.1 Spring WebFlux核心架构核心组件实际应用高级特性性能优化适用场景与限制 6.2.2 Akka&#xff08;Actor模型&#xff09;Actor模型基础基本用法高级特性响应式特性实现性能优化实际应用场景优势与挑战 6.2.3 Vert.x&#xff08;事件驱动&…

Python爬虫第7节-requests库的高级用法

目录 前言 一、文件上传 二、Cookies 三、会话维持 四、SSL证书验证 五、代理设置 六、超时设置 七、身份认证 八、Prepared Request 前言 上一节&#xff0c;我们认识了requests库的基本用法&#xff0c;像发起GET、POST请求&#xff0c;以及了解Response对象是什么。…

Maven的安装配置-项目管理工具

各位看官&#xff0c;大家早安午安晚安呀~~~ 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 今天我们来学习&#xff1a;Maven的安装配置-项目管理工具 目录 1.什么是Maven&#xff1f;Maven用来干什么的&#xff1f…

智能 SQL 优化工具 PawSQL 月度更新 | 2025年3月

&#x1f4cc; 更新速览 本月更新包含 21项功能增强 和 9项问题修复&#xff0c;重点提升SQL解析精度与优化建议覆盖率。 一、SQL解析能力扩展 ✨ 新增SQL语法解析支持 SELECT...INTO TABLE 语法解析&#xff08;3/26&#xff09; ALTER INDEX RENAME/VISIBLE 语句解析&#…

Ubuntu虚拟机编译安装部分OpenCV模块方法实现——保姆级教程

Ubuntu虚拟机的安装过程可以查看另一篇文章&#xff1a;VMware安装Ubuntu虚拟机实现COpenCV代码在虚拟机下运行教程-CSDN博客 目前我们已经下载好了OpenCV&#xff0c;这里以OpenCV4.5.2为例。 在内存要求尽可能小的情况下&#xff0c;可以尝试只编译安装代码中使用到的OpenC…

spring mvc @ResponseBody 注解转换为 JSON 的原理与实现详解

ResponseBody 注解转换为 JSON 的原理与实现详解 1. 核心作用 ResponseBody 是 Spring MVC 的一个注解&#xff0c;用于将方法返回的对象直接序列化为 HTTP 响应体&#xff08;如 JSON 或 XML&#xff09;&#xff0c;而不是通过视图解析器渲染为视图&#xff08;如 HTML&…