Как передать предыдущий кадр как Sampler2D во фрагментный шейдер в Vulkan?

В настоящее время я разрабатываю проект Vulkan и столкнулся с проблемой, которую не могу решить.

Мне нужно передать изображение предыдущего кадра в качестве sampler2D во фрагментный шейдер. Я следовал руководству Vulkan по настройке проекта, поэтому у меня правильно реализована цепочка обмена.

Моя идея заключалась в том, чтобы использовать vkCmdCopyImage для копирования текущего кадра, отображаемого в изображении цепочки обмена, в целевой VkImage перед началом рендеринга следующего кадра. Таким образом, я могу передать этот VkImage как sampler2D во фрагментный шейдер для рендеринга следующего кадра.

Во-первых, я хотел бы спросить, правильный ли мой подход или есть ли лучший способ справиться с этой ситуацией.

Если это решение осуществимо, я был бы признателен за некоторые рекомендации о том, что может быть не так с моим кодом. Ниже я предоставляю метод drawFrame(), который вызывается в основном цикле моего приложения, и методtexture(), который обрабатывает копирование изображения.

Проблема в том, что если я не вызываю метод текстуры(), все работает нормально, но у меня нет предыдущего кадра во фрагментном шейдере. Однако если я вызываю метод текстуры(), все работает без ошибок, но кажется, что VkImage никогда не обновляется.

void texture(int curImageIndex) {
    VkImage textureImage = getImage(); // destination image
    VkCommandBuffer commandBuffer = beginSingleTimeCommands();

    VkImageMemoryBarrier barrierSwapchainToTransfer = {};
    barrierSwapchainToTransfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrierSwapchainToTransfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    barrierSwapchainToTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
    barrierSwapchainToTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrierSwapchainToTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrierSwapchainToTransfer.image = swapChainImages[curImageIndex];
    barrierSwapchainToTransfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    barrierSwapchainToTransfer.subresourceRange.baseMipLevel = 0;
    barrierSwapchainToTransfer.subresourceRange.levelCount = 1;
    barrierSwapchainToTransfer.subresourceRange.baseArrayLayer = 0;
    barrierSwapchainToTransfer.subresourceRange.layerCount = 1;
    barrierSwapchainToTransfer.srcAccessMask = VK_ACCESS_NONE_KHR;
    barrierSwapchainToTransfer.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;

    vkCmdPipelineBarrier(
        commandBuffer,
        VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
        0,
        0, nullptr,
        0, nullptr,
        1, &barrierSwapchainToTransfer
    );

    VkImageMemoryBarrier barrierTextureToTransfer = {};
    barrierTextureToTransfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrierTextureToTransfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    barrierTextureToTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    barrierTextureToTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrierTextureToTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrierTextureToTransfer.image = textureImage;
    barrierTextureToTransfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    barrierTextureToTransfer.subresourceRange.baseMipLevel = 0;
    barrierTextureToTransfer.subresourceRange.levelCount = 1;
    barrierTextureToTransfer.subresourceRange.baseArrayLayer = 0;
    barrierTextureToTransfer.subresourceRange.layerCount = 1;
    barrierTextureToTransfer.srcAccessMask = VK_ACCESS_NONE_KHR;
    barrierTextureToTransfer.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

    vkCmdPipelineBarrier(
        commandBuffer,
        VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
        0,
        0, nullptr,
        0, nullptr,
        1, &barrierTextureToTransfer
    );

    VkImageCopy copyRegion = {};
    copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    copyRegion.srcSubresource.mipLevel = 0;
    copyRegion.srcSubresource.baseArrayLayer = 0;
    copyRegion.srcSubresource.layerCount = 1;
    copyRegion.srcOffset = { 0, 0, 0 };
    copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    copyRegion.dstSubresource.mipLevel = 0;
    copyRegion.dstSubresource.baseArrayLayer = 0;
    copyRegion.dstSubresource.layerCount = 1;
    copyRegion.dstOffset = { 0, 0, 0 };
    copyRegion.extent = { swapChainExtent.width, swapChainExtent.height, 1 };

    vkCmdCopyImage(
        commandBuffer,
        swapChainImages[curImageIndex], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
        textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
        1, &copyRegion
    );

    VkImageMemoryBarrier barrierTextureToShaderRead = {};
    barrierTextureToShaderRead.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrierTextureToShaderRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    barrierTextureToShaderRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    barrierTextureToShaderRead.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrierTextureToShaderRead.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrierTextureToShaderRead.image = textureImage;
    barrierTextureToShaderRead.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    barrierTextureToShaderRead.subresourceRange.baseMipLevel = 0;
    barrierTextureToShaderRead.subresourceRange.levelCount = 1;
    barrierTextureToShaderRead.subresourceRange.baseArrayLayer = 0;
    barrierTextureToShaderRead.subresourceRange.layerCount = 1;
    barrierTextureToShaderRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    barrierTextureToShaderRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

    vkCmdPipelineBarrier(
        commandBuffer,
        VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
        0,
        0, nullptr,
        0, nullptr,
        1, &barrierTextureToShaderRead
    );

    VkImageMemoryBarrier barrierSwapchainToPresent = {};
    barrierSwapchainToPresent.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrierSwapchainToPresent.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
    barrierSwapchainToPresent.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
    barrierSwapchainToPresent.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrierSwapchainToPresent.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrierSwapchainToPresent.image = swapChainImages[curImageIndex];
    barrierSwapchainToPresent.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    barrierSwapchainToPresent.subresourceRange.baseMipLevel = 0;
    barrierSwapchainToPresent.subresourceRange.levelCount = 1;
    barrierSwapchainToPresent.subresourceRange.baseArrayLayer = 0;
    barrierSwapchainToPresent.subresourceRange.layerCount = 1;
    barrierSwapchainToPresent.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
    barrierSwapchainToPresent.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;

    vkCmdPipelineBarrier(
        commandBuffer,
        VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        0,
        0, nullptr,
        0, nullptr,
        1, &barrierSwapchainToPresent
    );

    endSingleTimeCommands(commandBuffer);;
}
void drawFrame() {
    vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
    
    texture(currentFrame);

    uint32_t imageIndex;

    VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);

    if (result == VK_ERROR_OUT_OF_DATE_KHR) {
        recreateSwapChain();
        return;
    }
    else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
        throw std::runtime_error("failed to acquire swap chain image!");
    }


    if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
        vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
    }
    imagesInFlight[imageIndex] = inFlightFences[currentFrame];

    updateUniformBuffer(imageIndex);

    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
    VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; 
    submitInfo.waitSemaphoreCount = 1;
    submitInfo.pWaitSemaphores = waitSemaphores;
    submitInfo.pWaitDstStageMask = waitStages;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
    VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = signalSemaphores;

    vkResetFences(device, 1, &inFlightFences[currentFrame]);

    if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { 
        throw std::runtime_error("failed to submit draw command buffer!");
    }

    VkPresentInfoKHR presentInfo{}; 
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = signalSemaphores;

    VkSwapchainKHR swapChains[] = { swapChain };
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = swapChains;
    presentInfo.pImageIndices = &imageIndex;
    presentInfo.pResults = nullptr; 

    result = vkQueuePresentKHR(presentQueue, &presentInfo);

    if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR ||
        framebufferResized) {
        framebufferResized = false;
        recreateSwapChain();
    }
    else if (result != VK_SUCCESS) {
        throw std::runtime_error("failed to present swap chain image!");
    }

    currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Здесь несколько ошибок:

  1. Вы звоните texture() по номеру кадра, а не по индексу изображения. У вас может быть 3 изображения в вашей цепочке обмена, а максимальное количество кадров в полете равно 2. Это означает, что вы, вероятно, будете читать неправильное изображение цепочки обмена и никогда не будете читать изображение цепочки обмена 2 в этом случае. Кажется, вместо этого вы хотите пройти imageIndex.

  2. Вы звоните texture() перед рендерингом. Это означает, что вы, вероятно, будете читать изображение с неопределенным содержимым. Возможно, вы захотите вызвать его после отправки в очередь и до настоящего момента.

  3. Барьеры в texture() не совсем правильные. Вот версия, которая мне подошла. Я не реализовал шейдерное чтение изображения и просто использовал RenderDoc, чтобы убедиться, что копия работает (возможно, вы захотите сделать то же самое). Итак, параметры barrierTextureToShaderRead настроены для просмотра текстуры в RenderDoc. Возможно, вам придется оставить их такими, какими они были у вас, для использования шейдером.

     void texture(int curImageIndex) {
     VkImage textureImage = getImage(); // destination image
     VkCommandBuffer commandBuffer = beginSingleTimeCommands();
    
     VkImageMemoryBarrier barrierSwapchainToTransfer = {};
     barrierSwapchainToTransfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
     barrierSwapchainToTransfer.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;  // was: UNDEFINED
     barrierSwapchainToTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
     barrierSwapchainToTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrierSwapchainToTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrierSwapchainToTransfer.image = swapChainImages[curImageIndex];
     barrierSwapchainToTransfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
     barrierSwapchainToTransfer.subresourceRange.baseMipLevel = 0;
     barrierSwapchainToTransfer.subresourceRange.levelCount = 1;
     barrierSwapchainToTransfer.subresourceRange.baseArrayLayer = 0;
     barrierSwapchainToTransfer.subresourceRange.layerCount = 1;
     barrierSwapchainToTransfer.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;  // was: NONE
     barrierSwapchainToTransfer.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; 
    
     vkCmdPipelineBarrier(
         commandBuffer,
         VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,  // was: TOP, TRANSFER
         0,
         0, nullptr,
         0, nullptr,
         1, &barrierSwapchainToTransfer
     );
    
     VkImageMemoryBarrier barrierTextureToTransfer = {};
     barrierTextureToTransfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
     barrierTextureToTransfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
     barrierTextureToTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
     barrierTextureToTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrierTextureToTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrierTextureToTransfer.image = textureImage;
     barrierTextureToTransfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
     barrierTextureToTransfer.subresourceRange.baseMipLevel = 0;
     barrierTextureToTransfer.subresourceRange.levelCount = 1;
     barrierTextureToTransfer.subresourceRange.baseArrayLayer = 0;
     barrierTextureToTransfer.subresourceRange.layerCount = 1;
     barrierTextureToTransfer.srcAccessMask = VK_ACCESS_NONE_KHR;
     barrierTextureToTransfer.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    
     vkCmdPipelineBarrier(
         commandBuffer,
         VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,  // was: TOP, TRANSFER
         0,
         0, nullptr,
         0, nullptr,
         1, &barrierTextureToTransfer
     );
    
     VkImageCopy copyRegion = {};
     copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
     copyRegion.srcSubresource.mipLevel = 0;
     copyRegion.srcSubresource.baseArrayLayer = 0;
     copyRegion.srcSubresource.layerCount = 1;
     copyRegion.srcOffset = { 0, 0, 0 };
     copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
     copyRegion.dstSubresource.mipLevel = 0;
     copyRegion.dstSubresource.baseArrayLayer = 0;
     copyRegion.dstSubresource.layerCount = 1;
     copyRegion.dstOffset = { 0, 0, 0 };
     copyRegion.extent = { swapChainExtent.width, swapChainExtent.height, 1 };
    
     vkCmdCopyImage(
         commandBuffer,
         swapChainImages[curImageIndex], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
         textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
         1, &copyRegion
     );
    
     VkImageMemoryBarrier barrierTextureToShaderRead = {};
     barrierTextureToShaderRead.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
     barrierTextureToShaderRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
     barrierTextureToShaderRead.newLayout = VK_IMAGE_LAYOUT_GENERAL;// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
     barrierTextureToShaderRead.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrierTextureToShaderRead.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrierTextureToShaderRead.image = textureImage;
     barrierTextureToShaderRead.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
     barrierTextureToShaderRead.subresourceRange.baseMipLevel = 0;
     barrierTextureToShaderRead.subresourceRange.levelCount = 1;
     barrierTextureToShaderRead.subresourceRange.baseArrayLayer = 0;
     barrierTextureToShaderRead.subresourceRange.layerCount = 1;
     barrierTextureToShaderRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
     barrierTextureToShaderRead.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;//VK_ACCESS_SHADER_READ_BIT;
    
     vkCmdPipelineBarrier(
         commandBuffer,
         VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,  // was: TRANSFER, FRAGMENT_SHADER
         0,
         0, nullptr,
         0, nullptr,
         1, &barrierTextureToShaderRead
     );
    
     VkImageMemoryBarrier barrierSwapchainToPresent = {};
     barrierSwapchainToPresent.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
     barrierSwapchainToPresent.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
     barrierSwapchainToPresent.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
     barrierSwapchainToPresent.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrierSwapchainToPresent.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrierSwapchainToPresent.image = swapChainImages[curImageIndex];
     barrierSwapchainToPresent.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
     barrierSwapchainToPresent.subresourceRange.baseMipLevel = 0;
     barrierSwapchainToPresent.subresourceRange.levelCount = 1;
     barrierSwapchainToPresent.subresourceRange.baseArrayLayer = 0;
     barrierSwapchainToPresent.subresourceRange.layerCount = 1;
     barrierSwapchainToPresent.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
     barrierSwapchainToPresent.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    
     vkCmdPipelineBarrier(
         commandBuffer,
         VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,  // was: TRANSFER, BOTTOM
         0,
         0, nullptr,
         0, nullptr,
         1, &barrierSwapchainToPresent
     );
    
     endSingleTimeCommands(commandBuffer);;
    

    }

Наконец, посмотрите этот скриншот кода в качестве примера чтения образа цепочки обмена.

Спасибо за ваш ответ. Сейчас работает, но не в полной мере. Проблема в том, что VkImage корректно обновляется, но кажется размытым. Я попробовал использовать vkCmdBlitImage, так как мое устройство поддерживает копирование, и я получил лучший результат, но изображение все равно размыто. Знаете ли вы, какие могут быть возможные причины?

Nabucodonosor 23.07.2024 11:07

Позже я понял, что вам, вероятно, нужно использовать функцию текстуры так, как она была у меня, а затем перевести текстуру в режим чтения шейдера, когда она будет использоваться в следующем кадре. Но я думаю, вы это поняли. Что касается размытости, я могу только догадываться, что вы накладываете текстуру на гораздо меньшую область экрана, и вам следует посмотреть настройки сэмплера. Возможно, для него установлено значение «ближайший», и вам нужно что-то вроде линейного для лучшей выборки. Ожидается некоторая потеря деталей.

Karl Schultz 23.07.2024 12:19

Другие вопросы по теме

Похожие вопросы

Найдите наименьший многоугольник с определенным ребром на двумерном графе Дейкстры
Самый быстрый алгоритм заполнения перекрывающихся прямоугольников пикселей
Wes_palettes: Как получить шестнадцатеричные коды цветов из палитры Весандерсона, если для нее установлено непрерывное количество цветов, превышающее предопределенные
SDL_Quit ошибка сегментации
Почему этот треугольный шейдер, который я написал, разбивается на четыре?
Принципы и практика программирования Страуструпа, 3-е издание, графика: почему передача функции обратного вызова в Window::timer_wait дает неожиданные результаты?
Шкала разрешения экрана в игре JavaFx
Как я могу использовать PowerShell для определения активной видеокарты с максимальной емкостью видеопамяти?
Gnuplot: установить соотношение сторон осей X и Y на трехмерном графике
Невозможно отобразить определенное изображение, декодированное из строки Base 64