操作系统:Windows8.1
  
  显卡:Nivida GTX965M
  
  开发工具:Visual Studio 2017
  
  Introduction
  
  我们现在可以将任意属性传递给每个顶点的顶点着色器使用。但是全局变量呢?我们将会从本章开始介绍3D图形相关的内容,并需要一个模型视图投影矩阵。我们确实可以将它一顶点的方式包含,但是这非常浪费带宽、内存,并且需要我们在变换的时候更新顶点缓冲区的数据。这种变换通常发生在每一帧。
  
  在Vulkan中正确处理此问题的途径是使用资源描述符。描述符是着色器资源访问诸如缓冲区和图像这样资源的一种方式。我们需要设置一个包含转换矩阵的缓冲区,并使顶点着色器通过描述符访问它们。描述符的使用由三部分组成:
  
  在管线创建时指定描述符的布局结构
  
  从描述符对象池中分配描述符集合
  
  在渲染阶段绑定描述符集合
  
  描述符布局指定了管线访问的资源的类型,就像渲染通道指定附件的类型一样。描述符集合指定将绑定到描述符的实际缓冲区或映射资源。就像帧缓冲区为绑定渲染通道的附件而指定实际的图像视图一样。描述符集合就像顶点缓冲区和帧缓冲区一样被绑定到绘制命令。
  
  有许多类型的描述符,但在本章中,我们将使用统一的缓冲区对象uniform buffer objects(UBO)。我们将会在后面的章节中讨论其他类型的描述符,但基本过程是一样的。假设我们有一个数据,我们希望顶点着色器拥有一个这样的C结构体:
  
  struct UniformBufferObject {
  
  glm::mat4 model;
  
  glm::mat4 view;
  
  glm::mat4 proj;
  
  };
  
  我们可以拷贝数据到VkBuffer并在顶点着色器中通过uniform buffer object描述访问它们:
  
  复制代码
  
  layout(binding = 0) uniform UniformBufferObject {
  
  mat4 model;
  
  mat4 view;
  
  mat4 proj;
  
  } ubo;
  
  void main() {
  
  gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
  
  fragColor = inColor;
  
  }
  
  复制代码
  
  我们会在每一帧更新模型,视图和投影矩阵,使前一章的矩形以3D旋转。
  
  Vertex shader
  
  修改顶点着色器包含像上面指定的统一缓冲区对象uniform buffer object。假设大家比较熟悉MVP变换。如果不是这样,可以看第一章中提到的内容了解。
  
  复制代码
  
  #version 450
  
  #extension GL_ARB_separate_shader_objects : enable
  
  layout(binding = 0) uniform UniformBufferObject {
  
  mat4 model;
  
  mat4 view;
  
  mat4 proj;
  
  } ubo;
  
  layout(location = 0) in vec2 inPosition;
  
  layout(location = 1) in vec3 inColor;
  
  layout(location = 0) out vec3 fragColor;
  
  out gl_PerVertex {
  
  vec4 gl_Position;
  
  };
  
  void main() {
  
  gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
  
  fragColor = inColor;
  
  }
  
  复制代码
  
  需要注意的是uniform, in 和 out它们声明的顺序无关紧要。binding指令与location属性指令类似。我们将在描述符布局中引用此绑定。gl_Position使用变换矩阵计算裁剪坐标中最终的位置信息。
  
  Descriptor set layout
  
  下一步在C++应用层定义UBO数据结构,并告知Vulkan在顶点着色器使用该描述符。
  
  struct UniformBufferObject {
  
  glm::mat4 model;
  
  glm::mat4 view;
  
  glm::mat4 proj;
  
  };
  
  我们可以使用GLM中的与着色器中结构体完全匹配的数据类型。矩阵中的数据与着色器预期的二进制数据兼容,所以我们可以稍后将一个UniformBufferObject通过memcpy拷贝到VkBuffer中。
  
  我们需要在管线创建时,为着色器提供关于每个描述符绑定的详细信息,就像我们为每个顶点属性和location索引做的一样。我们添加一个新的函数来定义所有这些名为createDescritorSetLayout的信息。考虑到我们会在管线中使用,它应该在管线创建函数之前调用。
  
  复制代码
  
  void initVulkan() {
  
  ...
  
  createDescriptorSetLayout();
  
  createGraphicsPipeline();
  
  ...
  
  }
  
  ...
  
  void createDescriptorSetLayout() {
  
  }
  
  复制代码
  
  每个绑定都会通过VkDescriptorSetLayoutBinding结构体描述。
  
  void createDescriptorSetLayout(www.wmylcs.com ) {
  
  VkDescriptorSetLayoutBinding uboLayoutBinding = {};
  
  uboLayoutBinding.binding = 0;
  
  uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  
  uboLayoutBinding.descriptorCount = 1;
  
  }
  
  前两个字段指定着色器中使用的binding和描述符类型,它是一个UBO。着色器变量可以表示UBO数组,descriptorCount指定数组中的数值。比如,这可以用于骨骼动画中的每个骨骼变换。我们的MVP 变换是一个单UBO对象,所以我们使用descriptorCount为1。
  
  uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
  
  我们也需要指定描述符在着色器哪个阶段被引用。stageFlags字段可以是VkShaderStage标志位或VK_SHADER_STAGE_ALL_GRAPHICS的组合。在我们的例子中,我们仅仅在顶点着色器中使用描述符。
  
  uboLayoutBinding.pImmutableSamplers = nullptr; /www.wmyl88.com// Optional
  
  pImmutableSamplers字段仅仅与图像采样的描述符有关,我们会在后面的内容讨论。现在可以设置为默认值。
  
  所有的描述符绑定都会被组合在一个单独的VkDescriptorSetLa www.wmyl11.com/ yout对象。定义一个新的类成员变量pipelineLayout:
  
  VkDescriptorSetLayout descript www.feifanshifan8.cn orSetLayout;
  
  VkPipelineLayout pipelineLayout;
  
  我们使用vkCreateDescriptorSetLayout创建。这个函数接受一个简单的结构体VkDescriptorSetLayoutCreateInfo,该结构体持有一个绑定数组。
  
  复制代码
  
  VkDescriptorSetLayoutCreateInfo www.wmyl15.com/ layoutInfo = {};
  
  layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
  
  layoutInfo.bindingCount = 1;
  
  layoutInfo.pBindings = &uboLayoutBinding;
  
  if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
  
  throw std::runtime_error("failed to create descriptor set layout!");
  
  }
  
  复制代码
  
  我们需要在创建管线的时候指定描述符集合的布局,用以告知Vulkan着色器将要使用的描述符。描述符布局在管线布局对象中指定。修改VkPipelineLayoutCreateInfo引用布局对象:
  
  VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
  
  pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
  
  pipelineLayoutInfo.setLayoutCount = 1;
  
  pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
  
  到这里可能会有疑问,为什么可以在这里指定那么多的描述符布局集合,因为一个包含了所有的绑定。我们将在下一章回顾一下,我们将在其中查看描述符对象池和描述符集合。
  
  描述符布局应该在程序退出前始终有效:
  
  复制代码
  
  void cleanup() {
  
  cleanupSwapChain();
  
  vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
  
  ...
  
  }
  
  复制代码
  
  Uniform buffer
  
  在下一章节我们会为着色器重点包含UBO的缓冲区,但是首先要创建该缓冲区。在每一帧中我们需要拷贝新的数据到UBO缓冲区,所以存在一个暂存缓冲区是没有意义的。在这种情况下,它只会增加额外的开销,并且可能降低性能而不是提升性能。
  
  添加类成员uniformBuffer和uniformBufferMemory:
  
  VkBuffer indexBuffer;
  
  VkDeviceMemory indexBufferMemory;
  
  VkBuffer uniformBuffer;
  
  VkDeviceMemory uniformBufferMemory;
  
  同样需要添加新的函数createUniformBuffer来分配缓冲区,并在createIndexBuffer之后调用。
  
  复制代码
  
  void initVulkan() {
  
  ...
  
  createVertexBuffer();
  
  createIndexBuffer();
  
  createUniformBuffer();
  
  ...
  
  }
  
  ...
  
  void createUniformBuffer() {
  
  VkDeviceSize bufferSize = sizeof(UniformBufferObject);
  
  createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffer, uniformBufferMemory);
  
  }
  
  复制代码
  
  我们要写一个单独的函数来更新uniform缓冲区,确保每一帧都有更新,所以在这里不会有vkMapMemory。UBO的数据将被用于所有的绘制使用,所以包含它的缓冲区只能在最后销毁:
  
  复制代码
  
  void cleanup() {
  
  cleanupSwapChain();
  
  vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
  
  vkDestroyBuffer(device, uniformBuffer, nullptr);
  
  vkFreeMemory(device, uniformBufferMemory, nullptr);
  
  ...
  
  }
  
  复制代码
  
  Updating uniform data
  
  添加新的函数updateUniformBuffer并在main loop中调用:
  
  复制代码
  
  void mainLoop() {
  
  while (!glfwWindowShouldClose(window)) {
  
  glfwPollEvents();
  
  updateUniformBuffer();
  
  drawFrame();
  
  }
  
  vkDeviceWaitIdle(device);
  
  }
  
  ...
  
  void updateUniformBuffer() {
  
  }
  
  复制代码
  
  该函数会在每一帧中创建新的变换矩阵以确保几何图形旋转。我们需要引入新的头文件使用该功能:
  
  #define GLM_FORCE_RADIANS
  
  #include <glm/glm.hpp>
  
  #include <glm/gtc/matrix_transform.hpp>
  
  #include <chrono>
  
  glm/gtc/matrix_transform.cpp头文件对外提供用于生成模型变换矩阵的gl::rotate,视图变换矩阵的 glm:lookAt 和 投影变换矩阵的 glm::perspective。GLM_FORCE_RADIANS定义是必要的,它确保像 glm::rotate 这样的函数使用弧度制作为参数,以避免任何可能的混淆。
  
  chrono标准库的头文件对外提供计时功能。我们将使用它来确保集合旋转每秒90度,无论帧率如何。
  
  void updateUniformBuffer() {
  
  static auto startTime = std::chrono::high_resolution_clock::now();
  
  auto currentTime = std::chrono::high_resolution_clock::now();
  
  float time = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count() / 1000.0f;
  
  }
  
  updateUniformBuffer函数将有关时间计算的逻辑开始,它会以毫秒级的精度计算渲染开始后的时间(秒为单位)。如果需要更准确的时间,则可以使用 std::chrono::microseconds 并除以 1e6f,这是 1000000.0 的缩写。
  
  我们在UBO中定义模型,视图和投影变换矩阵。模型变换将会围绕Z-axis旋转,并使用 time 变量更新旋转角度:
  
  UniformBufferObject ubo = {};
  
  ubo.model = glm::rotate(glm::mat4(), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
  
  glm::rotate 函数对现有的变换矩阵进行旋转,它使用旋转角度和旋转轴作为参数。glm::mat4() 默认的构造返回归一化的矩阵。使用 time * glm::radians(90.0f) 可以实现每秒90度的旋转目的。
  
  ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
  
  对于视图变换,我们决定以45度从上观察几何图形。glm::lookAt 函数以眼睛位置,中心位置和上方向为参数。
  
  ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float) swapChainExtent.height, 0.1f, 10.0f);
  
  选择使用FOV为45度的透视投影。其他参数是宽高比,近裁剪面和远裁剪面。重要的是使用当前的交换链扩展来计算宽高比,以便在窗体调整大小后参考最新的窗体宽度和高度。
  
  ubo.proj[1][1] *= -1;
  
  GLM最初是为OpenGL设计的,它的裁剪坐标的Y是反转的。修正该问题的最简单的方法是在投影矩阵中Y轴的缩放因子反转。如果不这样做图像会被倒置。
  
  现在定义了所有的变换,所以我们可以将UBO中的数据复制到uniform缓冲区。这与我们对顶点缓冲区的操作完全相同,除了没有暂存缓冲区:
  
  void* data;
  
  vkMapMemory(device, uniformBufferMemory, 0, sizeof(ubo), 0, &data);
  
  memcpy(data, &ubo, sizeof(ubo));
  
  vkUnmapMemory(device, uniformBufferMemory);
  
  使用UBO将并不是经常变化的值传递给着色器是非常有效的方式。相比传递一个更小的数据缓冲区到着色器中,更有有效的方式是使用常量。我们在未来的章节中会看到这些。

《权限系列shiro+cas》---封装公共验证模块的更多相关文章

  1. spring + shiro + cas 实现sso单点登录

    sso-shiro-cas spring下使用shiro+cas配置单点登录,多个系统之间的访问,每次只需要登录一次,项目源码 系统模块说明 cas: 单点登录模块,这里直接拿的是cas的项目改了点样 ...

  2. 七、spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制

    1.安装cas-server-3.5.2 官网:https://github.com/apereo/cas/releases/tag/v3.5.2 下载地址:cas-server-3.5.2-rele ...

  3. 从零开始实现asp.net MVC4框架网站的用户登录以及权限验证模块 详细教程

    从零开始实现asp.net MVC4框架网站的用户登录以及权限验证模块 详细教程   用户登录与权限验证是网站不可缺少的一部分功能,asp.net MVC4框架内置了用于实现该功能的类库,只需要简单搭 ...

  4. shiro实战系列(五)之Authentication(身份验证)

    建议学习shiro读读官方文档,虽然不一定读的懂,但是建议要大致浏览,心中有个大概,这样对于学习还是有一定帮助 官网地址:https://shiro.apache.org/ Authenticatio ...

  5. 分享api接口验证模块

    一.前言 权限验证在开发中是经常遇到的,通常也是封装好的模块,如果我们是使用者,通常指需要一个标记特性或者配置一下就可以完成,但实际里面还是有许多东西值得我们去探究.有时候我们也会用一些开源的权限验证 ...

  6. Ansible系列(二):选项和常用模块

    html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...

  7. 单点登录(十八)----cas4.2.x客户端增加权限控制shiro

    我们在上面章节已经完成了cas4.2.x登录启用mongodb的验证方式. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 也完成了获取管理员身份属性 ...

  8. springboot+shiro+cas实现单点登录之shiro端搭建

    github:https://github.com/peterowang/shiro-cas 本文如有配置问题,请查看之前的springboot集成shiro的文章 1.配置ehcache缓存,在re ...

  9. 手把手实现Java权限(1)-Shiro介绍

    功能介绍 Authentication :身份认证/登录.验证用户是不是拥有对应的身份:  Authorization :授权,即权限验证.验证某个已认证的用户是否拥有某个权限:即推断用  户能否做事 ...

随机推荐

  1. 基于Ubuntu交叉编译FFmpeg Windows SDK

    写在前面 FFmpeg是一个开源且跨平台的音视频解决方案,集采集.转码.流式化为一身,项目的libavcodec编解码模块和libavformat媒体格式模块,支持非常非常丰富的编解码格式和容器封装格 ...

  2. bootstrap 左右框多项选择示例

    bootstrap 左右选择框,左边框是未选项,右边框是已选择项,提供单选,全选按钮,以及取消已选项,如图示:

  3. UML Design Via Visual Studio-Sequence Diagram

    本文主要介绍在Visual Studio中设计时序图,内容如下: 何时使用时序图 时序图元素介绍 条件.循环在时序图中的使用 直接通过代码生成时序图 一.何时使用时序图 当要查看单个用例内若干对象的行 ...

  4. bzoj 4066 简单题——KDtree(带重构)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4066 带部分重构的KDtree.就是那个替罪羊树思想的. 写了对拍,调了半天,发现忘了 re ...

  5. bzoj 3998 弦论 —— 后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3998 关于相同子串算一个还是算多个,其实就是看一种状态的 right 集合是否加上 Pare ...

  6. MySQL5.7出现Your password has expired. To log in you must change it using a client that supports expir

    今天晚上本来想写bootstrap-fileinput插件集成fastdfs的文章,但是刚启动idea里面的QiYuAdmin就出现了错误: Your password has expired. To ...

  7. debian上安装codeblocks

    1.查看linux的版本uname -a 2.在官网上下载稳定版的codeblocks(www.codeblocks.org) 3.解压codeblocks后,进入到文件夹中用root身份执行dpkg ...

  8. selenium webdriver frame操作,跳进跳出

    如果有两个平级的frame,跳进一个以后操作完成再操作第二个,这种情况要先跳出来,再跳进另外一个frame 跳出语句:browser.switch_to_default_content() #codi ...

  9. ThinkPHP5.1的公共函数

    最初使用ThinkPHP3.2.3的时候,我们自己定义的公共函数常常放置于 \Common\function.php ThinkPHP5.1 公共函数 项目公用的会放在 \application\co ...

  10. HDOJ(1018)

    Big Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...