Vulkan Tutorial 8 深度缓冲

news2024/12/25 8:53:36

目录

26 三维几何图形

        深度图像和视图

27 显式转换深度图像

渲染通道

帧缓冲区

清除值

深度和模版状态

处理窗口调整大小


26 三维几何图形

到目前为止,我们所处理的几何体是投射到三维的,但它仍然是完全平面的。在这一章中,我们要给位置添加一个Z坐标,为三维网格做准备。我们将使用这第三个坐标在当前的正方形上放置一个正方形,看看几何体不按深度排序时出现的问题。

改变 “顶点”结构以使用三维矢量作为位置,并更新相应的VkVertexInputAttributeDescription中的format

attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;

更新顶点着色器以接受和转换3D坐标作为输入。之后别忘了重新编译!

layout(location = 0) in vec3 inPosition;

...

void main() {
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}

//更新vertices容器以包括Z坐标:

const std::vector<Vertex> vertices = {
    {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
    {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
    {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
    {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}

    //使用Z坐标为-0.5f,并为额外的正方形添加适当的索引:
    {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.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, 0.0f, 1.0f}, {1.0f, 1.0f}},
    {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}
};

const std::vector<uint16_t> indices = {
    0, 1, 2, 2, 3, 0,
    4, 5, 6, 6, 7, 4
};

现在运行你的程序,你会看到类似于埃舍尔插图的东西。

问题是,下层正方形的碎片被画在上层正方形的碎片之上,仅仅是因为它在索引数组中排在后面。有两种方法可以解决这个问题。

  • 按深度从后往前排序所有的绘制调用
  • 使用深度缓冲器进行深度测试

第一种方法通常用于绘制透明对象,因为与顺序无关的透明是一个难以解决的难题。然而,使用深度缓冲器更普遍地解决了按深度排序片段的问题。

深度缓冲区是一个额外的附件,它存储每个位置的深度,就像颜色附件存储每个位置的颜色一样。每次光栅化器产生一个片断时,深度测试将检查新片断是否比前一个片断更接近。如果不是,那么新的片段就会被丢弃。通过深度测试的片段会把它自己的深度写到深度缓冲区中。可以从片段着色器中操纵这个值,就像你可以操纵颜色输出一样。

#define GLM_FORCE_RADIANS
//由GLM生成的透视投影矩阵将使用OpenGL深度范围,默认为-1.0到1.0。我们需要使用
//GLM_FORCE_DEPTH_ZERO_TO_ONE'定义将其配置为使用Vulkan的0.0’到`1.0’范围。
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

深度图像和视图

深度附件是基于图像的,就像颜色附件一样。不同的是,互换链不会自动为我们创建深度图像。我们只需要一个深度图像,因为一次只运行一个绘制操作。深度图像将再次需要三方面的资源:图像、内存和图像视图。

void createDepthResources() {
//创建一个深度图像是相当直接的。它应该具有与颜色附件相同的分辨率,由交换链的范围定义,
//适合于深度附件的图像使用,最佳的平铺和设备本地内存

    

}

与纹理图像不同,我们不一定需要一个特定的格式,因为我们不会直接从程序中访问纹理。它只需要有一个合理的精度,在现实世界的应用中,至少有24比特是常见的。有几种格式可以满足这个要求。

  • vk_format_d32_sfloat。用于深度的32位浮点数
  • vk_format_d32_sfloat_s8_uint: 32位带符号的浮点数,用于深度和8位模版组件
  • VK_FORMAT_D24_UNORM_S8_UINT:用于深度和8位网板组件的24位浮点数。

我们可以简单地采用VK_FORMAT_D32_SFLOAT格式,因为对它的支持是非常普遍的(见硬件数据库),但在可能的情况下,为我们的应用增加一些额外的灵活性也是不错的。我们要写一个函数findSupportedFormat,它按照从最理想到最不理想的顺序接收一个候选格式列表,并检查哪个是第一个被支持的:

VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
    for (VkFormat format : candidates) {
        VkFormatProperties props;
        vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);

        if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
            return format;
        } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
            return format;
        }
    }

    throw std::runtime_error("failed to find supported format!");
}

VkFormatProperties 结构包含三个字段。

  • linearTilingFeatures:支持线性铺排的使用情况
  • optimalTilingFeatures: 支持最优铺排的使用情况
  • bufferFeatures: 支持缓冲区的用例

我们现在要用这个函数来创建一个findDepthFormat辅助函数,以选择一个有深度组件的格式,支持作为深度附件使用:

VkFormat findDepthFormat() {
    return findSupportedFormat(
        {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT},
        VK_IMAGE_TILING_OPTIMAL,
        VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
    );
}

//添加一个简单的辅助函数,告诉我们所选择的深度格式是否包含模版组件:

bool hasStencilComponent(VkFormat format) {
    return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
}

//调用函数从createDepthResources中找到一个深度格式:

VkFormat depthFormat = findDepthFormat();

//现在我们有了调用createImage和createImageView辅助函数的所有必要信息。:

createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
depthImageView = createImageView(depthImage, depthFormat);

createImageView函数目前假定子资源总是VK_IMAGE_ASPECT_COLOR_BIT,所以我们需要把这个字段变成一个参数:

VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) {
    ...
    viewInfo.subresourceRange.aspectMask = aspectFlags;

}

//更新对该函数的所有调用,以使用正确的方面:

swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT);
...
depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
...
textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);

    ...

27 显式转换深度图像

我们不需要将图像的布局显式转换为深度附件,因为我们将在渲染过程中处理这一点。

在 createDepthResources 函数的末尾调用 transitionImageLayout,如下所示:

transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);

//未定义的布局可以用作初始布局,因为没有重要的现有深度图像内容。我们需要更新 transitionImageLayout 中的一些逻辑以使用正确的子资源方面:

if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;

    if (hasStencilComponent(format)) {
        barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
    }
} else {
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}

//最后,添加正确的访问掩码和管线阶段:

if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
    barrier.srcAccessMask = 0;
    barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

    sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

    sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
    destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
    barrier.srcAccessMask = 0;
    barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

    sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
} else {
    throw std::invalid_argument("unsupported layout transition!");
}

渲染通道

我们现在要修改createRenderPass以包括一个深度附件。首先指定VkAttachmentDescription:

VkAttachmentDescription depthAttachment{};
depthAttachment.format = findDepthFormat();
//format应该与深度图像本身相同。这次我们不关心存储深度数据(storeOp
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
//我们不关心之前的深度内容,所以我们可以使用VK_IMAGE_LAYOUT_UNDEFINED作为initialLayout。
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

VkAttachmentReference depthAttachmentRef{};
depthAttachmentRef.attachment = 1;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

为第一个(也是唯一的)子通道添加一个对附件的引用:

subpass.pColorAttachments = &colorAttachmentRef;
subpass.pDepthStencilAttachment = &depthAttachmentRef;

与颜色附件不同,一个子通道只能使用一个深度(+stencil)附件。在多个缓冲区上做深度测试其实没有任何意义。

std::array<VkAttachmentDescription, 2> attachments = {colorAttachment, depthAttachment};
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;

//更新VkRenderPassCreateInfo结构以引用两个附件。

dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

 

帧缓冲区

下一步是修改framebuffer的创建,将深度图像绑定到深度附件上。进入createFramebuffers并指定深度图像视图为第二个附件:

std::array<VkImageView, 2> attachments = {
    swapChainImageViews[i],
    depthImageView
};

VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = renderPass;
framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
framebufferInfo.pAttachments = attachments.data();
framebufferInfo.width = swapChainExtent.width;
framebufferInfo.height = swapChainExtent.height;
framebufferInfo.layers = 1;

//你还需要移动对createFramebuffers的调用,以确保它是在深度图像视图真正被创建后调用的:

void initVulkan() {
    ...
    createDepthResources();
    createFramebuffers();
    ...
}

清除值

因为我们现在有VK_ATTACHMENT_LOAD_OP_CLEAR的多个附件,我们也需要指定多个清除值。转到recordCommandBuffer并创建一个VkClearValue结构的数组:

std::array<VkClearValue, 2> clearValues{};
clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
clearValues[1].depthStencil = {1.0f, 0};

renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
renderPassInfo.pClearValues = clearValues.data();
//注意clearValues的顺序应该与你的附件的顺序相同。

在Vulkan中,深度缓冲区的深度范围是0.01.0,其中1.0位于远视平面,0.0位于近视平面。深度缓冲区中每一点的初始值应该是最远的深度,也就是`1.0’。

深度和模版状态

深度附件现在已经可以使用了,但深度测试仍需要在图形管道中启用。它是通过VkPipelineDepthStencilStateCreateInfo结构配置的:

VkPipelineDepthStencilStateCreateInfo depthStencil{};
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
//depthTestEnable字段指定是否应将新片段的深度与深度缓冲区进行比较,看它们是否应被丢弃。
//depthWriteEnable字段指定是否应该将通过深度测试的新片段的深度实际写入深度缓冲区。
depthStencil.depthTestEnable = VK_TRUE;
depthStencil.depthWriteEnable = VK_TRUE;

//为保留或丢弃片段而进行的比较
depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;

//可选的深度绑定测试。基本上,这允许你只保留落在指定深度范围内的片段
depthStencil.depthBoundsTestEnable = VK_FALSE;
depthStencil.minDepthBounds = 0.0f; // Optional
depthStencil.maxDepthBounds = 1.0f; // Optional。

//
depthStencil.stencilTestEnable = VK_FALSE;
depthStencil.front = {}; // Optional
depthStencil.back = {}; // Optional
//最后三个字段配置了 stencil buffer 的操作

pipelineInfo.pDepthStencilState = &depthStencil;

如果你现在运行你的程序,那么你应该看到几何体的碎片现在已经正确排序了: 

 

处理窗口调整大小

深度缓冲区的分辨率应该在窗口调整大小时改变,以匹配新的颜色附件分辨率。在这种情况下,扩展recreateSwapChain函数来重新创建深度资源。

void recreateSwapChain() {
    int width = 0, height = 0;
    while (width == 0 || height == 0) {
        glfwGetFramebufferSize(window, &width, &height);
        glfwWaitEvents();
    }

清理操作应该发生在交换链清理功能中:

void cleanupSwapChain() {
    vkDestroyImageView(device, depthImageView, nullptr);
    vkDestroyImage(device, depthImage, nullptr);
    vkFreeMemory(device, depthImageMemory, nullptr);

    ...
}

 

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

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

相关文章

(学习日记)2023.4.22

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

【C++】关键字:static

关键字static 在这一章节&#xff0c;将对C中的关键字static做进一步介绍。 成员函数中的this指针 这里先附上之前写的一个complex类代码&#xff1a; //防卫式定义 #ifndef __MYCOMPLEX__ #define __MYCOMPLEX__//前置声明 class complex;//类声明 complex& __doapl (…

分享一个隐藏链接的样式

先上效果图&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>隐藏的链接按钮</title><style>.trapdoor {-webkit-transform: translateZ(0px);-webkit…

Unity Addressables热更流程

一、分组&#xff08;网上教程一大堆&#xff09; 二、构建 构建前设置&#xff1a; 1、分组设置。所有组做远端构建加载选择&#xff0c;RemoteBuildPath 。RemoteLoadPath 2、AddressableAssetSettings设置 3、构建 三、导出信息分析&#xff1a; 1、Assets同级目录下&#…

第十二届蓝桥杯国赛JavaB组题解

A. 整数范围 思路&#xff1a; 签到题。答案&#xff1a; 255 255 255 B. 纯质数 思路&#xff1a; 先用筛法筛出所有的质数&#xff0c;再根据题意判断&#xff0c;模板参考AcWing 数学知识。 代码&#xff1a; import java.io.BufferedReader; import java.io.IOExcepti…

Python相关环境变量配置和模拟手机app登录

【java环境变量配置】 接着&#xff0c;打开系统->高级系统设置->环境变量。新建一个JAVA_HOME C:\Program Files\Java\jdk1.8.0_201&#xff08;这里是你的安装路径&#xff0c;最好复制粘贴&#xff09; 然后在Path下添加 %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 然后…

【LeetCode每日一题】——1493.删掉一个元素以后全为 1 的最长子数组

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 滑动窗口 二【题目难度】 中等 三【题目编号】 1493.删掉一个元素以后全为 1 的最长子数组 …

wx.request net::ERR_TOO_MANY_REDIRECTS

文章目录 问题解决方法一&#xff1a;请求头里添加Cookies方法二&#xff1a;使用weapp-cookie 问题 使用微信小程序开发者工具开发小程序项目时&#xff0c;wx.request 返回总是报 net::ERR_TOO_MANY_REDIRECTS wx.request发起一个get请求&#xff0c;一直重定向直到请求失败…

Java Web——Session实现购物车

实验名称&#xff1a; Session实现购物车 实验目的&#xff1a; &#xff08;1&#xff09;掌握什么是Cookie对象&#xff0c;掌握Cookie对象的使用 &#xff08;2&#xff09;掌握什么是Session对象&#xff0c;掌握Session对象的使用 &#xff08;3&#xff09;掌握使用…

弗雷歇distanceDTW(动态时间规整)算法(附部分代码)

1、理论知识 1.1 什么是弗雷歇距离&#xff0c;它是用来干什么的&#xff1f; 费雷歇distance是求两个序列匹配之后的最大距离&#xff0c;这里需要明确a&#xff09;两个序列&#xff1b;b&#xff09;匹配过程&#xff1b;c&#xff09;最大距离的含义。 a&#xff09;两个…

【java】 【Springboot】 开发通用审核流程服务模块

完全自研一个极其轻量级 通用审核流程服务 可以进行流程&#xff0c;节点配置&#xff0c;流程&#xff0c;节点审核人权限配置 批量审核&#xff0c;批量驳回&#xff0c;审核&#xff0c;驳回&#xff0c;批量退回第一步&#xff0c;退回第一步等操作 涉及数据库表单字段

OSPF最优路径选择

路由比较 1、内部区域>区域间路由>NSSA1>Nssa2 2、如果只有Ex1、Ex2或者Nssa1、nNssa2开销类型。则Ex1>Ex2或者Nssa1>Nssa2 3、如果Ex1、Nssa1,Ex2和Nssa2,Ex1和Nssa1优于Ex2和Nssa2 4、如果外部开销加上内部开销,Ex1和Nssa1一样,则Ex1和Nssa1相同负载分担 5、如果外…

CVE-2016-1000027安全漏洞分析和解决方案

文章目录 前言复现问题漏洞分析解决办法相关阅读 前言 CVE-2016-1000027 安全漏洞&#xff0c;该漏洞在5.x的最新版本(5.3.27)依然存在,官方未给出任何解决办法。在spring-web6.0之后&#xff0c;则不存在该漏洞。 受影响范围:spring-web<6.0 解决办法&#xff1a;升级到sp…

【机器学习】分类问题和Logistic回归算法详解

在阅读本文前&#xff0c;请确保你已经掌握代价函数、假设函数等常用机器学习术语&#xff0c;最好已经学习线性回归算法&#xff0c;前情提要可参考https://blog.csdn.net/weixin_45434953/article/details/130593910 分类问题是十分广泛的一个问题&#xff0c;其代表问题是&…

图表控件LightningChart JS v.4.1已经发布!引入虚线模式

LightningChart JS是性能最高的JavaScript图表库&#xff0c;专注于实时数据可视化。是Web上性能最高的图表库具有出色的执行性能 - 使用高数据速率同时监控数十个数据源。 GPU加速和WebGL渲染确保您的设备的图形处理器得到有效利用&#xff0c;从而实现高刷新率和流畅的动画。…

固态硬盘基础知识:M.2 NVMe PCIe SATA的含义及区别

SATA接口 固态硬盘开始流行于笔记本电脑上&#xff0c;使用SATA接口&#xff0c;尺寸与笔记本硬盘相同&#xff0c;为2.5英寸。 那个时候&#xff0c;如果台式机需要加装固态硬盘&#xff0c;需要使用硬盘架&#xff0c;将固态硬盘安装在硬盘架上&#xff0c;然后再安装与机箱…

5.2.3 IP数据报(一)IP数据报的格式

5.2.3 IP数据报&#xff08;一&#xff09;IP数据报的格式 我们知道要想实现全球范围的通信除了地址要统一之外&#xff0c;数据格式也要统一&#xff0c;所以就有了IP分组&#xff0c;或者被称之为IP数据报&#xff0c;下面我们来学习IP分组的格式。如图 总体来说一个IP分组…

JetBrains的Java集成开发环境IntelliJ 2023版本在Linux系统的下载与安装配置教程

目录 前言一、IntelliJ安装二、使用配置总结 前言 IntelliJ IDEA Ultimate是一款功能强大的Java集成开发环境&#xff08;IDE&#xff09;。它提供了丰富的功能和工具&#xff0c;可以帮助开发人员更高效地编写、调试和部署Java应用程序。注&#xff1a;已在CentOS7.9和Ubuntu…

带大家来认识下SUMIF函数

如果您需要在Excel表格中对特定条件下的数据进行求和&#xff0c;那么SUMIF函数是一个非常有用的工具。SUMIF函数允许您在满足指定条件的情况下&#xff0c;将符合条件的单元格的值相加起来。在本文中&#xff0c;我们将向您介绍如何使用SUMIF函数来处理您的表格数据。 如下图…

想定制鞋子?先给脚部建模吧!

最近灌篮高手的电影正在火热上映中&#xff0c;湘北大战山王的比赛从漫画搬上了大荧幕&#xff0c;看得人热血沸腾&#xff0c;直呼“爷青回”&#xff0c;恨不得马上换上球衣球鞋&#xff0c;再去球场上挥汗如雨一番。 灌篮高手 同时NBA的季后赛也在如火如荼的进行中&#xf…