记录使用vulkan结合Objectarx开发的过程(三)——图形交互1

该文介绍了一个使用Vulkan图形API来渲染图形的方法,通过创建特殊的渲染通道和管线,不仅绘制颜色图像,还记录每个像素的坐标和对象ID。这使得能够通过鼠标选择图形及其特定部分。在渲染过程中,使用了多个图像附件,包括用于显示的颜色图像、深度图像、坐标及ID图像,并通过两个子通道分别处理绘制和转移操作。此外,文章还提供了顶点和片段着色器的示例代码,用于生成和处理图形信息。
摘要由CSDN通过智能技术生成

需要解决的问题

在屏幕上生成了渲染图形后希望能够通过鼠标选取图形上对应的对象及对应的部位等,因此在渲染时仅仅生成可见像素就远远不够,例如为了做一个绘制图形的程序,则需要通过鼠标选择已绘制的图形,且需要知道鼠标在选择时捕捉到了图形的相应坐标,为完成上述的功能,在渲染图形时不仅需要生成基本的图形还需生成图形中每个像素对应的坐标及像素所代表的对象ID。

需要准备的数据

渲染一副图片正常需要准备一张用于颜色的图片及一张用于深度的图片,另外,为了记录像素所对应的坐标及对象ID则需要额外的准备一个图片。为了满足后续的额外需求又多准备了一张颜色图片。所有的图片格式具体如下:

  1. 用于显示的图片(PresentImg):创建交换链后通过vkGetSwapchainImagesKHR获取
  2. 用于深度的图片(DepthImg):格式为VK_FORMAT_D32_SFLOAT
  3. 用于记录坐标及对象ID的图片(CoordinateImg):格式为VK_FORMAT_R32G32B32A32_SFLOAT
  4. 媒介图片(IntermediaryImg):格式同显示图片

实现思路

  1. 绘图阶段1:记录颜色至IntermediaryImg(媒介图片),记录坐标及对象ID至CoordinateImg(坐标及ID图片)。
  2. 绘图阶段2:将IntermediaryImg(媒介图片)转至PresentImg(显示图片)最终显示

具体实现

渲染通道

void MLINVulkanPrivate::createRenderPassForCoordinateAndObjectId()
{
	VkAttachmentDescription presentAttachment{}; //用于显示的颜色附件
	presentAttachment.format = mSwapchainDetails.mSwapchainFormat.format;
	presentAttachment.samples = mMsaaSamples;
	presentAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
	presentAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;//渲染结束后保存用于显示
	presentAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	presentAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
	presentAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	presentAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

	VkAttachmentDescription depthAttachment = {}; //用于深度的附件
	depthAttachment.format = VK_FORMAT_D32_SFLOAT;
	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;
	depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

	VkAttachmentDescription intermediaryAttachment{}; //用于媒介的颜色附件
	intermediaryAttachment.format = mSwapchainDetails.mSwapchainFormat.format;
	intermediaryAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
	intermediaryAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
	intermediaryAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;//渲染结束后保存用于后续读取
	intermediaryAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	intermediaryAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
	intermediaryAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	intermediaryAttachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;//后续用于采样

	VkAttachmentDescription coordinateAttachment{}; //用于记录坐标及ID的附件
	coordinateAttachment.format = VK_FORMAT_R32G32B32A32_SFLOAT;
	coordinateAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
	coordinateAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
	coordinateAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;//渲染结束后保存用于后续读取
	coordinateAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
	coordinateAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
	coordinateAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
	coordinateAttachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;//后续用于采样

	VkAttachmentReference colorAttachmentRef{};
	colorAttachmentRef.attachment = 0;
	colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

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

	VkAttachmentReference intermediaryAttachmentRef = {};
	intermediaryAttachmentRef.attachment = 2;
	intermediaryAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

	VkAttachmentReference coordinateAttachmentRef = {};
	coordinateAttachmentRef.attachment = 3;
	coordinateAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

	QVector<VkAttachmentReference> vecReferences0 = { 
		VkAttachmentReference{
			2,	//uint32_t attachment;
			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL //VkImageLayout layout;
		},
		VkAttachmentReference{
			3,	//uint32_t         attachment;
			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL//VkImageLayout layout;
		}};
		
	VkSubpassDescription subpass_0{};//子通道0,将场景渲染至媒介图片并且记录坐标及ID
	subpass_0.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
	subpass_0.colorAttachmentCount = static_cast<uint32_t>(vecReferences0.size());
	subpass_0.pColorAttachments = vecReferences0.constData();
	subpass_0.pDepthStencilAttachment = &depthAttachmentRef;

	QVector<VkAttachmentReference> vecReferences1 = {
		VkAttachmentReference{
			2,	//uint32_t attachment;
			VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL//VkImageLayout layout;
		},
		VkAttachmentReference{
			3,	//uint32_t attachment;
			VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL//VkImageLayout layout;
		} };
	VkSubpassDescription subpass_1{};//子通道1,将媒介图片转至显示图片用于展示
	subpass_1.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
	subpass_1.colorAttachmentCount = 1;
	subpass_1.pColorAttachments = &colorAttachmentRef;
	subpass_1.inputAttachmentCount = static_cast<uint32_t>(vecReferences1.size());
	subpass_1.pInputAttachments = vecReferences1.constData();//此处将子通道0中绘制得到的媒介图片用作子通道1的输入附件

	QVector<VkSubpassDependency> vecDependency = {
		VkSubpassDependency{
			0,	//uint32_t srcSubpass;
			1,	//uint32_t dstSubpass;
			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,//VkPipelineStageFlags srcStageMask;
			VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, //VkPipelineStageFlags dstStageMask;
			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,//VkAccessFlags srcAccessMask;
			VK_ACCESS_SHADER_READ_BIT,//VkAccessFlags dstAccessMask;
			VK_DEPENDENCY_BY_REGION_BIT//VkDependencyFlags dependencyFlags;
		}
	};

	QVector<VkSubpassDescription> vecSubpassDes = { subpass_0, subpass_1 };
	std::array<VkAttachmentDescription, 4> attachments = { presentAttachment, depthAttachment, intermediaryAttachment, coordinateAttachment };
	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 = static_cast<uint32_t>(vecSubpassDes.size());
	renderPassInfo.pSubpasses = vecSubpassDes.constData();
	renderPassInfo.dependencyCount = 1;
	renderPassInfo.pDependencies = vecDependency.constData();

	if (vkCreateRenderPass(mDevice, &renderPassInfo, nullptr, &mRenderPassForCoordinateAndObjID) != VK_SUCCESS)
	{
		throw std::runtime_error("failed to create render pass!");
	}
}

渲染管线

创建多通道对应的渲染管线时由于每个子通道所对应的输出图片数量不同,那么相应的混合状态数量也不相同,因此需要开启相应的设备特性,这个通过在创建逻辑设备时设置VkPhysicalDeviceFeatures.independentBlend = VK_TRUE时实现。在创建管线时对应的混合状态具体如下:

  1. 子通道0对应的管线
QVector< VkPipelineColorBlendAttachmentState> step0BlendStates = {
	VkPipelineColorBlendAttachmentState{
		VK_TRUE,
		VK_BLEND_FACTOR_SRC_ALPHA,	
		VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,	
		VK_BLEND_OP_ADD,	//VkBlendOp colorBlendOp;
		VK_BLEND_FACTOR_SRC_ALPHA,	
		VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,	
		VK_BLEND_OP_ADD,	//VkBlendOp alphaBlendOp;
		0xF},
	VkPipelineColorBlendAttachmentState{
		VK_FALSE,                                                                                               
		VK_BLEND_FACTOR_ONE,	//坐标和像素不需要考虑透明混合,记录最近显示的即可
		VK_BLEND_FACTOR_ZERO,	
		VK_BLEND_OP_ADD,	
		VK_BLEND_FACTOR_ONE,
		VK_BLEND_FACTOR_ZERO,
		VK_BLEND_OP_ADD,
		0xF}
};
VkPipelineColorBlendStateCreateInfo colorBlendStateStep0
{
	VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
	nullptr,
	0,			
	VK_FALSE,
	VK_LOGIC_OP_CLEAR,
	static_cast<uint32_t>(step0BlendStates.size()),
	step0BlendStates.constData(),
	1.0, 1.0, 1.0, 1.0
};
  1. 子通道1对应的管线
VkPipelineColorBlendStateCreateInfo cb;//在通道1中仅是将通道0绘制的图像复制过来,不用考虑颜色混合
memset(&cb, 0, sizeof(cb));
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
cb.blendConstants[0] = 1.0;
cb.blendConstants[1] = 1.0;
cb.blendConstants[2] = 1.0;
cb.blendConstants[3] = 1.0;
VkPipelineColorBlendAttachmentState att
{
	VK_FALSE,
	VK_BLEND_FACTOR_SRC_ALPHA,	
	VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
	VK_BLEND_OP_ADD,	
	VK_BLEND_FACTOR_SRC_ALPHA,
	VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,	
	VK_BLEND_OP_ADD,
	0xF
};
cb.attachmentCount = 1;
cb.pAttachments = &att;

子通道0相关着色器

  1. 顶点着色器
#version 440
#extension GL_ARB_separate_shader_objects : enable 
#extension GL_ARB_shading_language_420pack : enable

const vec3 cDiffuse = vec3(0.8, 0.8, 0.8);
const vec3 cSpacular = vec3(0.7, 0.7, 0.7);
const float cRoughness = 6;	

layout(location = 0) in vec3 i_pos;				//坐标
layout(location = 1) in vec3 i_nor;				//法向
layout(location = 2) in vec3 i_color;			//颜色
layout(location = 3) in float i_transparency;	//透明度	
layout(location = 4) in uint i_EntID;

layout(location = 0) out vec4 o_color;	//计算得到的最终颜色
layout(location = 1) out vec4 o_pos;

layout(std140, binding = 0) uniform UVal {
    mat4 u_MVP;					
	vec3 u_LightDir;	//光线反方向,单位向量
	vec3 u_Ambient;		//环境光强度系数
	vec3 u_CameraPos;	//相机位置
} uVal;

void main()
{
	gl_Position = uVal.u_MVP * vec4(i_pos, 1.0);
	vec3 diffuseFactor = max(0.0, dot(uVal.u_LightDir, i_nor)) * cDiffuse;
	vec3 eyeDir = normalize(uVal.u_CameraPos - i_pos);
	vec3 middleDir = normalize(eyeDir + uVal.u_LightDir);
	vec3 spacularFactor = max(0.0, pow(dot(middleDir, i_nor), cRoughness)) * cSpacular;
	vec3 finalFactor = uVal.u_Ambient + (diffuseFactor + spacularFactor) * uVal.u_Ambient;
	o_color = vec4(finalFactor * i_color, i_transparency);
	o_pos = vec4(i_pos, i_EntID);
}
  1. 片段着色器
#version 440
#extension GL_ARB_separate_shader_objects : enable 
#extension GL_ARB_shading_language_420pack : enable

layout(location = 0) in vec4 i_color;	
layout(location = 1) in vec4 i_pos;

layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec4 fragCoordinate;

void main()
{
    fragColor = i_color;
	fragCoordinate = i_pos;
}

子通道1相关着色器

  1. 顶点着色器
 #version 440
#extension GL_ARB_separate_shader_objects : enable 
#extension GL_ARB_shading_language_420pack : enable

layout(location = 0) in vec2 i_pos;		

void main()
{
	gl_Position = vec4(i_pos, 0.0, 1.0);
}

2.片段着色器

#version 440
#extension GL_ARB_separate_shader_objects : enable 
#extension GL_ARB_shading_language_420pack : enable

layout(location = 0) out vec4 fragColor;

layout (input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput inputColor;

//坐标和对象ID在后续应用中可以使用,比如增加鼠标选中的描边特效等,此处仅做预留
layout (input_attachment_index = 1, set = 0, binding = 1) uniform subpassInput inputPosition;

void main()
{
	fragColor = subpassLoad(inputColor);
}

运行效果

  1. 两个阶段绘制的图片
    在这里插入图片描述
  2. 坐标及ID图片中记录的值
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值