Link

Atualizando os dados uniform

Criamos uma nova função updateUniformBuffer e adicionamos uma chamada a partir da função startNextFrame antes da chamada para a função vkCmdDraw:

void Renderer::startNextFrame() {
	...
	updateUniformBuffer();

	m_deviceFunctions->vkCmdDraw(
		commandBuffer,
		static_cast<uint32_t>(m_object->model->vertices.size()),
		1,
		0,
		0
	);
	...
}

void Renderer::updateUniformBuffer()
{

}

Esta função irá gerar uma nova transformação a cada quadro para fazer a geometria girar.

void Renderer::updateUniformBuffer() {
	static QTime startTime(QTime::currentTime());

	float time = static_cast<float>(startTime.elapsed())/1000.0f;
}

A função updateUniformBuffer começará com alguma lógica para calcular o tempo em segundos desde que a renderização foi iniciada. Para isso, utilizamos a classe QTime que expõe funções para realizar a cronometragem precisa. Usaremos isso para garantir que a geometria gire 90 graus por segundo, independentemente da taxa de quadros. Devemos nos certificar de incluir o cabeçalho <QTime> em renderer.cpp.

Vamos agora definir as transformações do modelo, visão e projeção no UBO. A rotação do modelo será uma rotação simples em torno do eixo Z usando a variável de tempo:

UniformBufferObject ubo = {};
ubo.model.setToIdentity();
ubo.model.rotate(time * 90.0, QVector3D(0.0f, 0.0f, 1.0));

A função QMatrix4x4::setToIdentity define a matriz como uma matriz identidade. A função QMatrix4x4::rotate recebe como parâmetros o ângulo de rotação e o eixo de rotação. Usando um ângulo de rotação de time * 90.0 cumpre o objetivo de rotação de 90 graus por segundo.

QVector3D eye = QVector3D(1.0, 1.0, 1.0);
QVector3D center = QVector3D(0.0, 0.0, 0.0);
QVector3D up = QVector3D(0.0, 0.0, 1.0);

ubo.view.setToIdentity();
ubo.view.lookAt(eye, center, up);

Para a transformação da visão, decidimos observar a geometria de cima em um ângulo de 45 graus. A função QMatrix4x4::lookAt recebe como parâmetros a posição do observador (eye), a posição central (center) e o eixo para cima (up).

QSize swapChainImageSize = m_window->swapChainImageSize();
float aspectRatio = static_cast<float>(swapChainImageSize.width()) / static_cast<float>(swapChainImageSize.height());
ubo.proj = m_window->clipCorrectionMatrix();
ubo.proj.perspective(45.0f, aspectRatio, 0.01f, 100.0f);

A classe QMatrix4x4 foi originalmente projetada para OpenGL, onde a coordenada Y das coordenadas de recorte é invertida. Ao pré-definir a matriz de projeção com QVulkanWindow::clipCorrectionMatrix(), os aplicativos podem continuar assumindo que Y está apontando para cima.

Usamos uma projeção em perspectiva. Para isso, utilizamos a função QMatrix4x4::perspective que recebe como parâmetros, respectivamente, o ângulo de visão, a proporção de tela (aspect ratio) e os planos de visualização próximo e distante. É importante usar o tamanho atual das imagem do swap chain para calcular a proporção para levar em consideração a nova largura e a altura da janela após um redimensionamento. Essa informação é obtida da função QVulkanWindow::swapChainImageSize().

Todas as transformações estão definidas agora, para que possamos copiar os dados para o uniform buffer do objeto 3D. Isso acontece exatamente da mesma maneira que fizemos com os buffers de vértice, exceto sem um staging buffer:

quint8 *data;
VkDevice device = m_window->device();
m_deviceFunctions->vkMapMemory(device, m_object->uniformBufferMemory, 0, sizeof(UniformBufferObject), 0, reinterpret_cast<void **>(&data));

memcpy(data, ubo.model.constData(), 64);
memcpy(data + 64, ubo.view.constData(), 64);
memcpy(data + 128, ubo.proj.constData(), 64);

m_deviceFunctions->vkUnmapMemory(device, m_object->uniformBufferMemory);

Utilizamos aqui o tipo quint8*, em vez de void**, para usarmos aritmética de ponteiro para definir os valores das matrizes concatenados dentro da variável data. Cada matriz possui o tamanho de 64 bytes.


Anterior Próximo