Vulkan开发实战详解 学习笔记 - 绘制方式,点绘制、线段绘制和三角形绘制

三角形条带与扇面

三角形条带

BeltData.h

#ifndef VULKANEXBASE_BELTDATA_H
#define VULKANEXBASE_BELTDATA_H

class BeltData
{
public:
    static float* vdata;
    static int dataByteCount;
    static int vCount;
    static double toRadians(double d);
    static void  genVertexData();
};
#endif

BeltData.cpp

#include <../main_task/BeltData.h>
#include <vector>
#include <math.h>
#include <string.h>
#include "BeltData.h"

const double PI=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170680;

float* BeltData::vdata;
int BeltData::dataByteCount;
int BeltData::vCount;

double BeltData::toRadians(double d)
{
    double e=d*PI/180;
    return e;
}
void  BeltData::genVertexData(){								//生成顶点数据的方法
    int n = 6;                              						//条带切割份数
    //始化了计算三角形条带顶点数据所需的各个辅助变量并创建了顶点数据存储数组。
    vCount=2*(n+1);                							//计算顶点数量
    dataByteCount=vCount*6* sizeof(float);						//计算顶点数据所占总字节数
    float angdegBegin = -90;									//设置条带起始度数
    float angdegEnd = 90;     								//设置条带结束度数
    float angdegSpan = (angdegEnd-angdegBegin)/n;				//计算条带每份度数
    vdata=new float[vCount*6];									//创建顶点数据数组
    int count=0;											//辅助索引
   //行为生成三角形条带顶点位置及顶点颜色数据的代码
    for(float angdeg=angdegBegin; angdeg<=angdegEnd; angdeg+=angdegSpan) {
        double angrad=toRadians(angdeg);						//计算当前弧度
        vdata[count++]=(float) (-0.6f*50*sin(angrad));			//此切割角度第1个顶点的x坐标
        vdata[count++]=(float) (0.6f*50*cos(angrad));			//此切割角度第1个顶点的y坐标
        vdata[count++]=0;								//此切割角度第1个顶点的z坐标
        vdata[count++]=1;								//此切割角度第1个顶点颜色R分量
        vdata[count++]=1;								//此切割角度第1个顶点颜色G分量
        vdata[count++]=1;								//此切割角度第1个顶点颜色B分量
        vdata[count++]=(float) (-50*sin(angrad));				//此切割角度第2个顶点的x坐标
        vdata[count++]=(float) (50*cos(angrad));				//此切割角度第2个顶点的y坐标
        vdata[count++]=0;								//此切割角度第2个顶点的z坐标
        vdata[count++]=0;								//此切割角度第2个顶点颜色R分量
        vdata[count++]=1;								//此切割角度第2个顶点颜色G分量
        vdata[count++]=1;								//此切割角度第2个顶点颜色B分量
}}

在这里插入图片描述

CircleData.cpp

#include "CircleData.h"
#include "BeltData.h"
#include <vector>
#include <math.h>
#include <string.h>

float* CircleData::vdata;
int CircleData::dataByteCount;
int CircleData::vCount;
void  CircleData::genVertexData(){
	//初始化了计算扇面顶点数据所需的各个辅助变量并创建了顶点数据存储数组
    int n = 10;												//扇形切割份数
    vCount=n+2;											//顶点数量
    dataByteCount=vCount*6* sizeof(float);						//顶点数据所占总字节数
    vdata=new float[vCount*6];									//创建顶点数据数组
    float angdegSpan=360.0f/n;								//扇形每份度数
    int count=0;											//辅助索引
    //给出了扇面中心点的坐标及颜色数据
    vdata[count++] = 0;										//第一个顶点X坐标
    vdata[count++] = 0;										//第一个顶点Y坐标
    vdata[count++] = 0;										//第一个顶点Z坐标
    vdata[count++] = 1;										//第一个顶点颜色R分量
    vdata[count++] = 1;										//第一个顶点颜色G分量
    vdata[count++] = 1;										//第一个顶点颜色B分量
    //循环生成了扇面周围一圈顶点的坐标及颜色数据
    for(float angdeg=0; ceil(angdeg)<=360; angdeg+=angdegSpan) {	//循环生成周围其他顶点的数据
        double angrad=BeltData::toRadians(angdeg);				//当前弧度
        vdata[count++]=(float) (-50*sin(angrad));					//当前顶点x坐标
        vdata[count++]=(float) (50*cos(angrad));					//当前顶点y坐标
        vdata[count++]=0;									//当前顶点z坐标
        vdata[count++] = 0;									//当前顶点颜色R分量
        vdata[count++] = 1;									//当前顶点颜色G分量
        vdata[count++] = 0;									//当前顶点颜色B分量
}}

在这里插入图片描述

shade

#version 400
#extension GL_ARB_separate_shader_objects : enable//启动GL_ARB_separate_shader_objects
#extension GL_ARB_shading_language_420pack : enable//启动GL_ARB_shading_language_420pack
layout (push_constant) uniform constantVals 
{			 //推送常量块
    mat4 mvp;//总变换矩阵
} myConstantVals;
layout (location = 0) in vec3 pos;//传入的物体坐标系顶点位置
layout (location = 1) in vec3 color;//传入的顶点颜色
layout (location = 0) out vec3 vcolor;//传到片元着色器的顶点颜色
out gl_PerVertex { vec4 gl_Position;};				//输出接口块
void main() 
{									//主函数
    gl_Position = myConstantVals.mvp * vec4(pos,1.0);	//计算顶点最终位置
    vcolor=color;//传递顶点颜色给片元着色器
}

在这里插入图片描述

BeltData2.cpp

#include <../main_task/BeltData.h>
#include <vector>
#include <math.h>
#include <string.h>
#include "BeltData.h"

const double PI=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170680;

float* BeltData::vdata;
int BeltData::dataByteCount;
int BeltData::vCount;

double BeltData::toRadians(double d)
{
    double e=d*PI/180;
    return e;
}

void  BeltData::genVertexData(){								//生成顶点数据的方法
	int n1 = 3;                                                     //第一个条带切割份数
	int n2 = 5;                                                     //第二个条带切割份数
	vCount = 2 * (n1 + n2 + 2) + 2;                                 //计算总顶点数
    dataByteCount=vCount*6* sizeof(float);						//计算顶点数据所占总字节数			//计算条带每份度数
    vdata=new float[vCount*6];									//创建顶点数据数组
	float angdegBegin1 = 0;                                         //第一个条带起始度数
	float angdegEnd1 = 90;                                          //第一个条带结束度数
	float angdegSpan1 = (angdegEnd1 - angdegBegin1) / n1;            //第一个条带每份度数
	float angdegBegin2 = 180;                                           //第二个条带起始度数
	float angdegEnd2 = 270;                                         //第二个条带结束度数
	float angdegSpan2 = (angdegEnd2 - angdegBegin2) / n2;            //第二个条带每份度数
    int count=0;											//辅助索引
	for (float angdeg = angdegBegin1; angdeg <= angdegEnd1; angdeg += angdegSpan1) {
        double angrad=toRadians(angdeg);						//计算当前弧度
		vdata[count++] = (float)(-0.6f * 80 * sin(angrad));                //外围大圆上的点X坐标
		vdata[count++] = (float)(0.6f * 80 * cos(angrad));                //外围大圆上的点Y坐标
        vdata[count++]=0;								//此切割角度第1个顶点的z坐标
        vdata[count++]=1;								//此切割角度第1个顶点颜色R分量
        vdata[count++]=1;								//此切割角度第1个顶点颜色G分量
        vdata[count++]=1;								//此切割角度第1个顶点颜色B分量
		vdata[count++] = (float)(-80 * sin(angrad));                    //内圈小圆上的点X坐标
		vdata[count++] = (float)(80 * cos(angrad));                    //内圈小圆上的点Y坐标
        vdata[count++]=0;								//此切割角度第2个顶点的z坐标
        vdata[count++]=0;								//此切割角度第2个顶点颜色R分量
        vdata[count++]=1;								//此切割角度第2个顶点颜色G分量
        vdata[count++]=1;								//此切割角度第2个顶点颜色B分量
	}
	vdata[count++] = vdata[count - 6];            //重复第一批三角形的最后一个顶点数据
	vdata[count++] = vdata[count - 6];
	vdata[count++] = 0;
	vdata[count++] = 1;
	vdata[count++] = 0;
	vdata[count++] = 0;
    //第2个条带数据的生成与第一个条带相似
    //初始化了计算条带顶点数据的辅助变量,包括顶点数量、顶点数据总字节数、两个条带度数范围以及条带分割的每份度数等,同时还创建了顶点数据数组。
	for (float angdeg = angdegBegin2; angdeg <= angdegEnd2; angdeg += angdegSpan2) {
		double angrad = toRadians(angdeg);                    //当前弧度
		if (angdeg == angdegBegin2) {            //重复第二批三角形的第一个顶点数据
			vdata[count++] = (float)(-0.6f * 80 * sin(angrad));            //顶点X坐标
			vdata[count++] = (float)(0.6f * 80 * cos(angrad));            //顶点Y坐标
			vdata[count++] = 0;                                //顶点Z坐标
			vdata[count++] = 0;
			vdata[count++] = 1;
			vdata[count++] = 0;    //顶点颜色
		}
		//大圆上的点
		vdata[count++] = (float)(-0.6f * 80 * sin(angrad));//顶点坐标
		vdata[count++] = (float)(0.6f * 80 * cos(angrad));
		vdata[count++] = 0;
		vdata[count++] = 1;
		vdata[count++] = 1;
		vdata[count++] = 1;
		//小圆上的点
		vdata[count++] = (float)(-80 * sin(angrad));//顶点坐标
		vdata[count++] = (float)(80 * cos(angrad));
		vdata[count++] = 0;
		vdata[count++] = 0;
		vdata[count++] = 1;
		vdata[count++] = 1;
	}
}

相比于上一个案例主要是改变了顶点的位置。物体不再是整体由一批连续三角形组成,而是分两批连续三角形组成。第一批的最后一个顶点和第二批的第一个顶点在顶点序列中重复了

在这里插入图片描述

在这里插入图片描述

索引法绘制

vkCmdDraw前面的案例中都是调用vkCmdDraw方法来执行物体的绘制,此方法是按照传入渲染管线的顶点本身的顺序及选用的绘制方式将顶点组织成图元进行绘制的,也可以称之为顶点法。

vkCmdDrawIndexed方法在绘制时不但要将顶点序列传入渲染管线,还需要将索引序列传入渲染管线。绘制时管线根据索引序列中的索引值从顶点序列中取出对应的顶点,并根据当前选用的绘制方式组织成图元进行绘制,图4-33很好地说明了这个问题。

采用索引法(vkCmdDrawIndexed)进行绘制时可以有效地减少重复顶点数据,有重复时只需要提供重复的索引号即可。。而每个索引值只需要一个整数,相比一个顶点数据需要3个(也可能是更多的)整数或浮点数可以节省不少空间。

CircleData.h

#ifndef VULKANEXBASE_BELTDATA_H
#define VULKANEXBASE_BELTDATA_H

#include <cstdint>
//代码为在CircleData类头文件中添加的服务于索引数据的相关变量,包括索引数据指针、总字节数和索引数量。
class CircleData
{
public:
    static float* vdata;
    static int dataByteCount;
    static int vCount;
    static uint16_t* idata; //索引数据指针
    static int indexByteCount; //索引数据所占总字节数
    static int iCount; //索引数量
    static double toRadians(double d);
    static void  genVertexData();
};

#endif //TRIANGLESTRIP_CIRCLEDATA_H
#include <../main_task/CircleData.h>
#include <vector>
#include <math.h>
#include <string.h>
const double PI=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170680;

float* CircleData::vdata;
int CircleData::dataByteCount;
int CircleData::vCount;
uint16_t* CircleData::idata;
int CircleData::indexByteCount;
int CircleData::iCount;


double CircleData::toRadians(double d)
{
    double e=d*PI/180;
    return e;
}
//主要功能为生成要绘制的物体的顶点位置、颜色数据及绘制所需的索引数据。
void  CircleData::genVertexData(){
    int n = 10;												//扇形切割份数
    vCount=n+2;											//顶点数量
    dataByteCount=vCount*6* sizeof(float);						//顶点数据所占总字节数
    vdata=new float[vCount*6];									//创建顶点数据数组
    float angdegSpan=360.0f/n;								//扇形每份度数
    int count=0;											//辅助索引
	vdata[count++] = 0; vdata[count++] = 0; vdata[count++] = 0; //第一个顶点的坐标
	vdata[count++] = 1; vdata[count++] = 1; vdata[count++] = 1; //第一个顶点的颜色
    for(float angdeg=0; ceil(angdeg)<=360; angdeg+=angdegSpan) {	//循环生成周围其他顶点的数据
		double angrad = toRadians(angdeg); //当前弧度
		vdata[count++] = (float)(-30 * sin(angrad)); //顶点x 坐标
		vdata[count++] = (float)(30 * cos(angrad)); //顶点y 坐标
        vdata[count++]=0;									//当前顶点z坐标
		vdata[count++] = 0; vdata[count++] = 1; vdata[count++] = 0; //顶点颜色
	}
	iCount = 12; //索引数量
	indexByteCount = iCount * sizeof(uint16_t); //索引数据所占总字节数
	idata = new uint16_t[iCount]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1 }; //索引数据数组
}

用于生成物体数据的CircleData类后,还需要对物体绘制类DrawableObjectCommon进行修改,使其能够创建索引缓冲。首先要介绍的是对DrawableObjectCommon类声明的修改,主要是添加了与创建索引缓冲相关的变量和方法,并对drawSelf方法签名进行了修改,具体代码如下。

DrawableObjectCommonLight函数

//创建顶点数据缓冲、索引数据缓冲所需的诸多变量,对构造函数和绘制方法的签名进行了修改
//,添加了索引数据相关的入口参数,并提供了用于创建顶点数据缓冲、索引数据缓冲的两个方法。

//行用于接收逻辑设备指针、顶点数据数组首地址指针、顶点数量、索引数据数组首地址指针、索引数量等,并保存到对应成员变量中。
DrawableObjectCommonLight::DrawableObjectCommonLight( //构造函数
	float* vdataIn, int dataByteCount, int vCountIn, //传入的顶点数据相关参数
	uint16_t* idataIn, int indexByteCount, int iCountIn, //传入的索引数据相关参数
	VkDevice& device, VkPhysicalDeviceMemoryProperties& memoryroperties)
{
    pushConstantData = new float[16];					//推送常量数据数组的初始化
    //用于接收逻辑设备指针、顶点数据数组首地址指针、顶点数量、索引数据数组首地址指针、索引数量等,并保存到对应成员变量中。
	this->devicePointer = &device;
	this->vdata = vdataIn;
	this->vCount = vCountIn;
	this->idata = idataIn; //接收索引数据数组首地址指针并保存
	this->iCount = iCountIn; //接收索引数量并保存
    //调用相关方法分别创建顶点数据缓冲和索引数据缓冲,其中createVertexBuffer方法就是将之前创建顶点数据缓冲的过程封装而来。
    //createIndexBuffer方法基本也是如此,需要注意的是创建索引缓冲时需要将缓冲创建信息结构体中的index_buf_info.usage设置为VK_BUFFER_USAGE_INDEX_BUFFER_BIT。
    //用相关方法分别创建顶点数据缓冲和索引数据缓冲,其中createVertexBuffer方法就是将之前创建顶点数据缓冲的过程封装而来。
    //createIndexBuffer方法基本也是如此,需要注意的是创建索引缓冲时需要将缓冲创建信息结构体中的index_buf_info.usage设置为VK_BUFFER_USAGE_INDEX_BUFFER_BIT。
	createVertexBuffer(dataByteCount, device, memoryroperties); //调用方法创建顶点数据缓冲
	createIndexBuffer(indexByteCount, device, memoryroperties); //调用方法创建索引数据缓冲
}

​ createVertexBuffer(dataByteCount, device, memoryroperties); //调用方法创建顶点数据缓冲

void DrawableObjectCommonLight::createVertexBuffer(int dataByteCount, VkDevice& device, VkPhysicalDeviceMemoryProperties& memoryroperties)
{

	VkBufferCreateInfo buf_info = {};//构建缓冲创建信息结构体实例
	buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;//设置结构体类型
	buf_info.pNext = NULL;//自定义数据的指针
	buf_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;//缓冲的用途为顶点数据
	buf_info.size = dataByteCount;//设置数据总字节数
	buf_info.queueFamilyIndexCount = 0;//队列家族数量
	buf_info.pQueueFamilyIndices = NULL;//队列家族索引列表
	buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;//共享模式
	buf_info.flags = 0;//标志

	VkResult result = vkCreateBuffer(device, &buf_info, NULL, &vertexDatabuf);//创建缓冲
	assert(result == VK_SUCCESS);//检查缓冲创建是否成功

	VkMemoryRequirements mem_reqs;//缓冲内存需求
	vkGetBufferMemoryRequirements(device, vertexDatabuf, &mem_reqs);//获取缓冲内存需求
	assert(dataByteCount <= mem_reqs.size);//检查内存需求获取是否正确

	VkMemoryAllocateInfo alloc_info = {};//构建内存分配信息结构体实例
	alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;//结构体类型
	alloc_info.pNext = NULL;//自定义数据的指针
	alloc_info.memoryTypeIndex = 0;//内存类型索引
	alloc_info.allocationSize = mem_reqs.size;//内存总字节数

	VkFlags requirements_mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;//需要的内存类型掩码
	bool flag = memoryTypeFromProperties(memoryroperties, mem_reqs.memoryTypeBits, requirements_mask, &alloc_info.memoryTypeIndex);//获取所需内存类型索引
	if (flag)
	{
		printf("确定内存类型成功 类型索引为%d\n", alloc_info.memoryTypeIndex);
	}
	else
	{
		printf("确定内存类型失败!\n");
	}

	result = vkAllocateMemory(device, &alloc_info, NULL, &vertexDataMem);//为顶点数据缓冲分配内存
	assert(result == VK_SUCCESS);

	uint8_t *pData;//CPU访问时的辅助指针
	result = vkMapMemory(device, vertexDataMem, 0, mem_reqs.size, 0, (void **)&pData);//将设备内存映射为CPU可访问
	assert(result == VK_SUCCESS);//检查映射是否成功
	memcpy(pData, vdata, dataByteCount);//将顶点数据拷贝进设备内存
	vkUnmapMemory(device, vertexDataMem);//解除内存映射
	result = vkBindBufferMemory(device, vertexDatabuf, vertexDataMem, 0);//绑定内存与缓冲
	assert(result == VK_SUCCESS);

	vertexDataBufferInfo.buffer = vertexDatabuf;//指定数据缓冲
	vertexDataBufferInfo.offset = 0;//数据缓冲起始偏移量
	vertexDataBufferInfo.range = mem_reqs.size;//数据缓冲所占字节数
}

DrawableObjectCommonLight::createIndexBuffer

void DrawableObjectCommonLight::createIndexBuffer(int indexByteCount, VkDevice& device, VkPhysicalDeviceMemoryProperties& memoryroperties)
{
	//创建Buffer创建信息实例,为创建索引数据Buffer服务
	VkBufferCreateInfo index_buf_info = {};
	index_buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
	index_buf_info.pNext = NULL;
	index_buf_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
	index_buf_info.size = indexByteCount;
	index_buf_info.queueFamilyIndexCount = 0;
	index_buf_info.pQueueFamilyIndices = NULL;
	index_buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
	index_buf_info.flags = 0;
	VkResult result = vkCreateBuffer(device, &index_buf_info, NULL, &indexDatabuf);
	assert(result == VK_SUCCESS);

	//获取内存需求
	VkMemoryRequirements index_mem_reqs;
	vkGetBufferMemoryRequirements(device, indexDatabuf, &index_mem_reqs);
	assert(indexByteCount <= index_mem_reqs.size);
	//内存分配信息
	VkMemoryAllocateInfo index_alloc_info = {};
	index_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
	index_alloc_info.pNext = NULL;
	index_alloc_info.memoryTypeIndex = 0;
	index_alloc_info.allocationSize = index_mem_reqs.size;
	VkFlags index_requirements_mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
	//获取所需内存类型索引
	bool index_flag = memoryTypeFromProperties(memoryroperties, index_mem_reqs.memoryTypeBits, index_requirements_mask, &index_alloc_info.memoryTypeIndex);
	if (index_flag)
	{
		printf("确定内存类型成功 类型索引为%d\n", index_alloc_info.memoryTypeIndex);
	}
	else
	{
		printf("确定内存类型失败!\n");
	}
	//为顶点数据缓冲分配内存
	result = vkAllocateMemory(device, &index_alloc_info, NULL, &indexDataMem);
	assert(result == VK_SUCCESS);

	uint8_t *index_pData;
	//将显存映射为CPU可访问
	result = vkMapMemory(device, indexDataMem, 0, index_mem_reqs.size, 0, (void **)&index_pData);
	assert(result == VK_SUCCESS);

	//将顶点数据拷贝进显存
	memcpy(index_pData, idata, indexByteCount);
	//解除内存映射
	vkUnmapMemory(device, indexDataMem);

	//绑定内存与缓冲
	result = vkBindBufferMemory(device, indexDatabuf, indexDataMem, 0);
	assert(result == VK_SUCCESS);

	//记录Buffer Info
	indexDataBufferInfo.buffer = indexDatabuf;
	indexDataBufferInfo.offset = 0;
	indexDataBufferInfo.range = index_mem_reqs.size;
}

接下来介绍物体的绘制方法——drawSelf,其主要功能为将命令缓冲与管线、管线布局、描述集、顶点数据、索引数据进行绑定并执行绘制,具体代码如下。

void DrawableObjectCommonLight::drawSelf(VkCommandBuffer& cmd, VkPipelineLayout& pipelineLayout, VkPipeline& pipeline, VkDescriptorSet* desSetPointer, uint32_t sIndex, uint32_t eIndex)
{
	vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);//将当前使用的命令缓冲与指定管线绑定
	vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, desSetPointer, 0, NULL);//将命令缓冲、管线布局、描述集绑定
	const VkDeviceSize offsetsVertex[1] = { 0 };//顶点数据偏移量数组
	vkCmdBindVertexBuffers(//将顶点数据与当前使用的命令缓冲绑定
		cmd,				//当前使用的命令缓冲
		0,					//顶点数据缓冲在列表中的首索引
		1,					//绑定顶点缓冲的数量
		&(vertexDatabuf),	//绑定的顶点数据缓冲列表
		offsetsVertex		//各个顶点数据缓冲的内部偏移量
	);
	float* mvp = MatrixState3D::getFinalMatrix();					//获取总变换矩阵
	memcpy(pushConstantData, mvp, sizeof(float) * 16);				//将总变换矩阵拷贝入内存
	vkCmdPushConstants(cmd, pipelineLayout, 				//将常量数据送入管线
	VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 16, pushConstantData);
   // 调用vkCmdBindIndexBuffer方法将索引数据缓冲与使用的命令缓冲绑定。
    //调用vkCmdDrawIndexed方法执行索引绘制,由sIndex与eIndex确定了实际参与绘制的索引数量和范围。
	vkCmdBindIndexBuffer( //将顶点数据与当前使用的命令缓冲绑定
		cmd, //当前使用的命令缓冲
		indexDatabuf, //索引数据缓冲
		0, //索引数据缓冲首索引
		VK_INDEX_TYPE_UINT16); //索引数据类型
	//调用vkCmdDrawIndexed方法执行索引绘制,
    vkCmdDrawIndexed( //执行索引绘制 //
		cmd, //当前使用的命令缓冲
		eIndex - sIndex, //索引数量 //由sIndex与eIndex确定了实际参与绘制的索引数量和范围。
		1, //需要绘制的实例数量
		sIndex, //绘制用起始索引
		0, //顶点数据偏移量
		0); //需要绘制的第1 个实例的索引
}

绘制方法

void MyVulkanManager::drawObject()
{
	FPSUtil::init();//初始化FPS计算
	while (MyVulkanManager::loopDrawFlag)//每循环一次绘制一帧画面
	{
		FPSUtil::calFPS();//计算FPS
		FPSUtil::before();//一帧开始

		//获取交换链中的当前帧索引
		VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, &currentBuffer);
		//为渲染通道设置当前帧缓冲
		rp_begin.framebuffer = framebuffers[currentBuffer];

		vkResetCommandBuffer(cmdBuffer, 0);//恢复命令缓冲到初始状态
		result = vkBeginCommandBuffer(cmdBuffer, &cmd_buf_info);//启动命令缓冲

		MyVulkanManager::flushUniformBuffer();//将当前帧相关数据送入一致变量缓冲
		MyVulkanManager::flushTexToDesSet();//更新绘制用描述集

		vkCmdBeginRenderPass(cmdBuffer, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);//启动渲染通道
		MatrixState3D::pushMatrix();										//保护现场
		MatrixState3D::rotate(xAngle, 1, 0, 0);								//绕X轴旋转xAngle
		MatrixState3D::rotate(yAngle, 0, 1, 0);								//绕Y轴旋转yAngle

		MatrixState3D::pushMatrix();									//保护现场
		MatrixState3D::translate(0, 50, 0); //沿Y 轴正方向平移50
		cirForDraw->drawSelf(cmdBuffer, sqsCL->pipelineLayout, //绘制正十边形
			sqsCL->pipeline, &(sqsCL->descSet[0]), 0, CircleData::iCount);
		MatrixState3D::popMatrix();									//恢复现场

		MatrixState3D::pushMatrix();									//保护现场
		MatrixState3D::translate(0, -50, 0); //沿Y 轴负方向平移50
		cirForDraw->drawSelf(cmdBuffer, sqsCL->pipelineLayout,				//绘制扇形
			sqsCL->pipeline, &(sqsCL->descSet[0]), 0, CircleData::iCount / 2 + 1);
		MatrixState3D::popMatrix();									//恢复现场

		MatrixState3D::popMatrix();
		vkCmdEndRenderPass(cmdBuffer);//结束渲染通道
		result = vkEndCommandBuffer(cmdBuffer);//结束命令缓冲

		submit_info[0].waitSemaphoreCount = 1;//等待的信号量数量
		submit_info[0].pWaitSemaphores = &imageAcquiredSemaphore;//等待的信号量列表

		result = vkQueueSubmit(queueGraphics, 1, submit_info, taskFinishFence);//提交命令缓冲
		do {	//等待渲染完毕
			result =vkWaitForFences(device, 1, &taskFinishFence, VK_TRUE, FENCE_TIMEOUT);
		} while (result == VK_TIMEOUT);
		vkResetFences(device, 1, &taskFinishFence);//重置栅栏
		
		present.pImageIndices = &currentBuffer;//指定此次呈现的交换链图像索引
		result =vkQueuePresentKHR(queueGraphics, &present);//执行呈现
		FPSUtil::after(60);//限制FPS不超过指定的值
	}
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《Vulkan开发实战详解》电子书是一本关于Vulkan图形API的实用指南,专门面向游戏开发人员和图形程序员。Vulkan一个底层的图形API,提供了更高的性能和更好的跨平台兼容性。 这本书详细介绍了Vulkan的基础知识和常用技术,包括渲染管线、纹理映射、物理设备和内存管理等。读者可以通过这本书的引导和示例代码,了解如何使用Vulkan来创建高性能的图形应用程序。 作者通过实战经验,不仅讲解了如何使用Vulkan API进行图形渲染,还提供了一些实例和案例,让读者能够更好地理解和应用所学知识。此外,书中还解释了Vulkan与其他图形API(如OpenGL)的区别和优势,帮助读者更好地理解其应用背后的原理和设计。 这本电子书的特之一是提供了大量的代码示例和图形演示,使得读者能够更直观地理解和学习Vulkan的使用。通过实践和实例的学习,读者可以逐步掌握Vulkan开发技巧和最佳实践。 总的来说,《Vulkan开发实战详解》电子书适合有一定图形编程基础的开发人员学习和应用。通过阅读这本书,读者可以全面了解Vulkan的使用和应用,并能够利用其特性和优开发出高性能的图形应用程序。 ### 回答2: 《Vulkan开发实战详解》是一本关于Vulkan图形API的电子书。Vulkan是一种专为现代多核处理器和高性能显卡而设计的跨平台图形API。这本书涵盖了Vulkan的基本概念、核心功能和开发实践,旨在帮助读者全面了解和掌握这个强大的图形渲染技术。 该书的内容从Vulkan的基本概念出发,介绍了Vulkan的优势、特和架构。然后,它详细讲解了Vulkan的主要组件,如命令缓冲区、渲染流水线和资源管理。读者可以学习如何创建和配置这些组件以及它们的工作原理。 此外,该书还涵盖了Vulkan的高级特性,如多线程渲染、异步计算和着色器编译等。它详细解释了如何有效地利用这些特性,以提高应用程序的性能和效率。 《Vulkan开发实战详解》提供了丰富的实例代码和案例,供读者参考和实践。这些例子覆盖了Vulkan的各个方面,从简单的绘制三角形到复杂的光照和阴影效果。读者可以通过学习这些实例,逐步理解Vulkan开发流程和技巧。 总的来说,这本电子书提供了一个全面的指南,帮助读者深入理解和掌握Vulkan图形API的开发实践。无论是初学者还是有经验的开发者,都可以从中学习到丰富的知识和实用的技巧,以应对复杂的图形渲染需求。 ### 回答3: 《Vulkan开发实战详解》是一本针对Vulkan图形API进行深入学习和实践的电子书籍。Vulkan是一款跨平台、高性能的图形API,旨在提供更好的图形渲染和计算性能。该书系统地介绍了Vulkan的核心概念、基本原理和实际应用技巧,对于想要深入学习和了解Vulkan开发的人来说是一本不可多得的指南。 《Vulkan开发实战详解》从入门到进阶,侧重于实践和实际案例,帮助读者快速掌握Vulkan图形API的开发技巧。书中首先介绍了Vulkan的基础知识和API的使用方式,包括创建和管理Vulkan实例、设备、窗口等。接着,书中详细讲解了Vulkan绘制和渲染过程,包括命令缓冲、渲染通道、管线和着色器等方面的内容,读者可以通过具体的示例代码来理解和实践这些技术。 此外,书籍还特别关注Vulkan的性能优化和高级特性。阅读者可以学习如何通过使用Vulkan的高级功能来提高图形渲染的效率,包括多线程渲染和异步计算等。同时,书中还提供了一些实际案例的分析和解决方法,帮助读者解决在实际项目中可能遇到的问题和挑战。 总之,《Vulkan开发实战详解》是一本很好的Vulkan学习资源,通过阅读本书,读者将能够系统地学习和掌握Vulkan开发技巧和实践经验。无论是初学者还是有一定经验的开发者,都可以从中获得很多实用的知识和技巧,为自己的Vulkan开发之路提供指导和支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值