...
 
Commits (3)
......@@ -59,6 +59,7 @@ add_library(
src/Lib/src/Util/FileIo.cpp
src/Lib/src/Util/Logging.cpp
src/Lib/src/Util/VectorConversions.cpp
src/Lib/src/Util/PerlinGenerator.cpp
src/Lib/src/VkUtil/CompileShader.cpp
src/Lib/src/VkUtil/AccelerationStructureBase.cpp
......
......@@ -7,5 +7,49 @@ layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D texSampler;
void main() {
color = vec4(texture(texSampler, texCoord).rgb, 1.0);
vec2 offsets[9] = {
{-1, 1}, {0, 1}, {1, 1},
{-1, 0}, {0, 0}, {1, 0},
{-1, -1}, {0, -1}, {1, -1},
};
float edgeDetectKernel[9] = {
0, 1, 0,
1, -4, 1,
0, 1, 0,
};
float blurKernel[9] = {
1.0 / 84.0, 4.0 / 84.0, 1.0 / 84.0,
4.0 / 84.0, 16.0 / 84.0, 4.0 / 84.0,
1.0 / 84.0, 4.0 / 84.0, 1.0 / 84.0,
};
vec2 relativePixelSize = vec2(1.0) / textureSize(texSampler, 0);
vec4 samples[9];
for(int i = 0; i < 9; i++) {
samples[i] = texture(texSampler, texCoord + offsets[i] * relativePixelSize);
}
vec3 edge = vec3(0);
for(int i = 0; i < 9; i++) {
edge += samples[i].rgb * edgeDetectKernel[i];
}
vec3 blur = vec3(0);
for(int i = 0; i < 9; i++) {
blur += samples[i].rgb * blurKernel[i];
}
float l = length(edge);
vec3 res = vec3(0);
res = l * blur + (1 - l) * samples[5].rgb;
color = vec4(vec3(samples[5].a * 0.5), 1.0);
// color = vec4(res, 1.0);
}
\ No newline at end of file
#version 450
// #extension GL_GOOGLE_include_directive : enable
// #extension GL_ARB_separate_shader_objects : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) out vec2 texCoord;
......
......@@ -39,5 +39,5 @@ void main()
vec3 res = payload.value;
imageStore(image, ivec2(gl_LaunchIDNV.xy), vec4(res, 0));
imageStore(image, ivec2(gl_LaunchIDNV.xy), vec4(res, length(res)));
}
\ No newline at end of file
\section{Problem: Darstellung von Godrays/Strahlenbüscheln}
\subsection{Physikalische Grundlagen}
Wenn Licht an mikroskopisch kleinen Teilchen in einem Medium (z.B. Luft oder Wasser) gestreut wird und der
Beobachter im Schatten steht, zeichnen sich die Lichtstrahlen im Medium ab (siehe \ref{g:1}).
Wenn Licht an mikroskopisch kleinen Teilchen in einem Medium (z.B. Luft oder Wasser) gestreut wird und die
Lichtquelle größtenteils verdeckt ist, zeichnen sich die Lichtstrahlen im Medium ab (siehe \ref{g:1}).
Dieser Effekt wird Tyndall-Effekt \footnote{Siehe z.B. \url{https://de.wikipedia.org/wiki/Tyndall-Effekt}}
genannt. Diese sichtbaren Strahlen werden auch Strahlenbüschel genannt und verdienen den Namen
\gquote{Godrays} (also göttliche Strahlen) ihrer äußerst eindrucksvollen Wirkung.
Der Effekt lässt sich besonders gut während der Morgen- und Abenddämmerung beobachten oder wenn eine kleine
Wolke die Sonne verdeckt.
Auch unter Wasser oder wenn die Sonne in einen nebligen Wald scheint, lässt sich der Effekt
beobachten. Aufgrund der Eindrücklichkeit des Effekts kann er eine computergrafisch dargestellte Szene
genannt.
Diese sichtbaren Strahlen werden auch Strahlenbüschel genannt und verdienen den Namen
\gquote{Godrays} (also göttliche Strahlen) ihrer äußerst eindrucksvollen Wirkung und der Tatsache, dass sie in
der Natur vor allem aus Löchern in Wolkendecken auf die Erde herabscheinen.
Aufgrund der Eindrücklichkeit des Effekts kann er eine computergrafisch dargestellte Szene
extrem bereichern.
\\
Der Effekt lässt sich auch gut während der Morgen- und Abenddämmerung beobachten.
Auch unter Wasser oder wenn die Sonne in einen nebligen Wald scheint, lässt sich der Effekt
beobachten. Ein künstlich erzeugtes Beispiel ist die Sichtbarmachung de Verlaufs eines Lasers durch Staub
oder Puder. Der Effekt lässt sich auch umgekehrt beobachten, das heißt, dass der Großteil eines Mediums von
Strahlenbüscheln erfüllt ist, dadurch heller aussieht und es aussieht als würden Objekte Schatten im Medium
erzeugen. Hieraus folgt auch die Überlegung, dass die Streuung von Licht im Medium praktisch immer auftritt,
jedoch normalerweise unbemerkt bleibt, weil das Licht an jedem Punkt des Mediums ähnlich gestreut wird.
%
\begin{figure}[ht]
\centering
......
......@@ -69,7 +69,7 @@ Im Gegensatz zu der Arbeit aus
\ref{ss:RealTimeUnderwaterCausticsAndGodRays} werden hier physikalische Aspekte beleuchtet und eine Formel zur
Berechnung der Lichtintensität vorgestellt. Diese Formel wurde letztendlich
aus der Strahlungstransportgleichung entwickelt. Die Hauptaussage dieser Gleichung ist die Ermittlung des
eingestreuten Lichtes durch Integration entlang des Strahls. Das Integral lässt dabei als endliche Summe
eingestreuten Lichtes durch Integration entlang des Strahls. Das Integral lässt sich dabei als endliche Summe
annähern, was letztendlich zur Überlegung führt mehrere Samples entlang des Strahls zu verrechnen.\\
Neben den physikalischen Überlegungen stellt das Paper auch
eine Effizienzüberlegung an, die in einem Raytracing-Algorithmus integriert werden könnte:
......
\section{Umsetzung}
Es soll versucht werden Strahlenbüschel mittels Raytracing umzusetzen. Für die folgenden Überlegungen sei noch
der Begriff \gquote{dichtes Medium} definiert:
Es soll versucht werden, die realistische und physikalisch plausible Darstellung von Strahlenbüscheln mittels
Raytracing umzusetzen. Die zuvor genannten Quellen nutzen vor allem Post-Processing-Effekte und basieren auf
\gquote{traditionellen} Techniken wie Light-Space-Ray-Marching und Shadow-Mapping. Dies könnte auch mit
Raytracing so umgesetzt werden. Z.B. könnte Raytracing genutzt werden um einen G-Buffer zu erstellen und alle
Lichtberechnungen wie beim Deferred Shading auszuführen. Damit würde man jedoch die Möglichkeiten und Vorteile
von Raytracing (z.B. exakte Verdeckungsberechnungen) wegwerfen. Daher ist das Ziel, einen Algorithmus zu
entwickeln, der die Vorteile von Raytracing ausnutzt um Strahlenbüschel physikalisch plausibel darzustellen.
\\
Dazu muss auch eine geeignete Szene erstellt werden. Es kann dabei angenommen werden, dass das Medium, in
welchem eine signifikante Lichtstreuung geschieht entweder die gesamte Szene umfasst oder durch ein Mesh
modelliert ist (zum Beispiel für räumlich begrenzten Nebel).
Die folgenden beiden Ansätze erscheinen sinnvoll:
%
\paragraph{Dichtes Medium:} Ein Medium, in dem viele Partikel vorhanden sind, die das Licht streuen und
so die Entstehung von Strahlenbüscheln ermöglichen. Dies kann zum Beispiel staubige Luft, Nebel, oder einfach
Wasser sein. Das Medium wird durch ein Objekt in der Szene umrissen.
\subsection{Umsetzung mittels Sampling der Lichtquelle aus dem Medium heraus}
%
Ein möglicher Algorithmus zur Lösung des Problems besteht darin, beim Durchgang eines Strahls durch ein
dichtes Medium mittels Schattenfühlern abzutasten, inwiefern der Strahl von der Lichtquelle aus erreichbar ist
Medium mittels Schattenfühlern abzutasten, inwiefern der Strahl von der Lichtquelle aus erreichbar ist
und daraufhin zu berechnen, wieviel Licht entlang des Strahls zum Beobachter zurückgeworfen wird. Das grobe
Vorgehen ist das folgende:
%
\begin{enumerate}
\item Strahl trifft auf dichtes Medium
\item Strahl trifft auf Medium (falls Medium als Mesh modelliert ist)
%
\begin{enumerate}
\item Der Eintrittspunkt wird in der Ray-Payload vermerkt.
\item Der Eintrittspunkt wird in der Ray-Payload vermerkt. (Falls das Medium die gesamte Szene
umfasst ist der Eintrittspunkt der Augpunkt)
\end{enumerate}
%
\item Strahl trifft auf anderes Objekt oder tritt aus dichtem Medium aus
......@@ -48,7 +55,8 @@ Vorgehen ist das folgende:
%
Dabei ist natürlich vor allem auf die Performance zu achten. Je mehr Schattenfühler genutzt werden, desto
genauer wird das Ergebnis, desto teurer wird jedoch auch die Berechnung. Auch für die genaue Art der
Verrechnung der einzelnen Samples muss eine Formel entwickelt werden.
Verrechnung der einzelnen Samples muss eine Formel gesucht werden. Optimierungspotenzial liegt in redundanten
Schattenfühlern, bzw. viele ähnliche Schattenfühlern.
\subsection{Umsetzung mittels Berechnung von der Lichtquelle aus}
Der zweite Lösungsansatz verfolgt einen ähnlichen Ansatz wie Photon-Mapping, indem versucht wird die
......
......@@ -37,9 +37,9 @@
\input{components/preamble.tex}
\input{components/grundlagen.tex}
\input{components/problem.tex}
\input{components/quellen.tex}
\input{components/umsetzung.tex}
\input{components/bewertung.tex}
\input{components/quellen.tex}
\input{components/gliederung.tex}
\input{components/zeitplan.tex}
\end{document}
......
#pragma once
#include <random>
namespace lib
{
class PerlinGenerator
{
private:
uint32_t width;
uint32_t height;
std::default_random_engine engine;
std::uniform_real_distribution<float> rnd;
// std::vector<> grid;
double perlin(double x, double y) const;
public:
PerlinGenerator(uint32_t w, uint32_t h, uint32_t seed);
uint32_t operator()(uint32_t x, uint32_t y) const;
};
} // namespace lib
......@@ -9,10 +9,25 @@
namespace lib
{
/**
* @brief forwards declaration für CgContext.
*/
class CgContext;
/**
* @brief Kapselt alle Vulkan-spezifischen Einstellungen einer Textur.
*/
struct TextureCreateInfo
{
/**
* @brief Initialisiert eine neue Instanz der TextureCreateInfo-Klasse.
*
* @param queueFamilyIndices Eine Auflistung von Indizes, die angibt, von welchen Queues die Textur
* genutzt werden darf.
* @param format Das interne Format der Textur.
* @param usage Die Nutzungsmodi der Textur.
* @param layout Das Layout der Textur.
*/
inline TextureCreateInfo(std::vector<uint32_t> queueFamilyIndices = {},
vk::Format format = vk::Format{},
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits{},
......@@ -24,9 +39,22 @@ namespace lib
{
}
/**
* @brief @param queueFamilyIndices Eine Auflistung von Indizes, die angibt, von welchen Queues die
* Textur genutzt werden darf.
*/
std::vector<uint32_t> queueFamilyIndices;
/**
* @brief @param format Das interne Format der Textur.
*/
vk::Format format;
/**
* @brief @param usage Die Nutzungsmodi der Textur.
*/
vk::ImageUsageFlags usage;
/**
* @brief @param layout Das Layout der Textur.
*/
vk::ImageLayout layout;
};
......
......@@ -88,7 +88,7 @@ bool lib::CgContext::TryCreateRasterizerPipeline()
rasterizationStateCreateInfo.rasterizerDiscardEnable = false;
rasterizationStateCreateInfo.polygonMode = vk::PolygonMode::eFill;
rasterizationStateCreateInfo.lineWidth = 1.f;
rasterizationStateCreateInfo.frontFace = vk::FrontFace::eCounterClockwise;
rasterizationStateCreateInfo.frontFace = vk::FrontFace::eClockwise;
rasterizationStateCreateInfo.cullMode = vk::CullModeFlagBits::eBack;
rasterizationStateCreateInfo.depthBiasEnable = false;
......@@ -175,12 +175,12 @@ void lib::CgContext::CreateRasterizationDescriptorSets()
frame.rasterizationDescriptorSet = *setsIt++;
vk::DescriptorImageInfo rtImageInfo;
rtImageInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
// rtImageInfo.imageView = frame.raytracingTexture->GetView();
// rtImageInfo.sampler = frame.raytracingTexture->GetSampler();
rtImageInfo.imageLayout = vk::ImageLayout::eGeneral;
rtImageInfo.imageView = frame.raytracingTexture->GetView();
rtImageInfo.sampler = frame.raytracingTexture->GetSampler();
rtImageInfo.imageView = this->dummyTexture->GetView();
rtImageInfo.sampler = this->dummyTexture->GetSampler();
// rtImageInfo.imageView = this->dummyTexture->GetView();
// rtImageInfo.sampler = this->dummyTexture->GetSampler();
vk::WriteDescriptorSet rtImageWrite;
rtImageWrite.descriptorCount = 1;
......@@ -237,7 +237,7 @@ void lib::CgContext::CreateRasterizationRenderPass()
{
vk::AttachmentDescription colorAttachment;
colorAttachment.initialLayout = vk::ImageLayout::eUndefined;
colorAttachment.finalLayout = vk::ImageLayout::ePresentSrcKHR;
colorAttachment.finalLayout = vk::ImageLayout::eColorAttachmentOptimal;
colorAttachment.loadOp = vk::AttachmentLoadOp::eDontCare;
colorAttachment.storeOp = vk::AttachmentStoreOp::eStore;
colorAttachment.format = this->swapChainImageFormat;
......
......@@ -740,61 +740,59 @@ void lib::CgContext::UpdateCommandBuffer(uint32_t imageIndex)
frame.commandBuffer.begin(beginInfo);
// if (this->IsOk())
// {
// frame.commandBuffer.bindPipeline(vk::PipelineBindPoint::eRayTracingNV, this->raytracingStep.pipeline);
// frame.commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingNV,
// this->raytracingStep.pipelineLayout,
// 0, frame.raytracingDescriptorSet,
// {});
// TransitionImageLayout(frame.commandBuffer,
// frame.raytracingTexture->GetHandle(), this->swapChainImageFormat,
// vk::ImageLayout::eUndefined, vk::ImageLayout::eGeneral,
// vk::PipelineStageFlagBits::eTopOfPipe,
// vk::PipelineStageFlagBits::eRayTracingShaderNV);
// frame.ubo->ScheduleTransfer(frame.commandBuffer);
// frame.commandBuffer.pushConstants(this->raytracingStep.pipelineLayout,
// vk::ShaderStageFlagBits::eRaygenNV | vk::ShaderStageFlagBits::eClosestHitNV,
// 0, sizeof(this->pushConstant),
// &this->pushConstant);
// auto sbtEntrySize = this->physicalDeviceInfo.GetRaytracingProperties().shaderGroupBaseAlignment;
// frame.commandBuffer.traceRaysNV(this->shaderBindingTableBuffer->GetBufferHandle(), this->shaderOffsets.rayGen * sbtEntrySize,
// this->shaderBindingTableBuffer->GetBufferHandle(), this->shaderOffsets.miss * sbtEntrySize, sbtEntrySize,
// this->shaderBindingTableBuffer->GetBufferHandle(), this->shaderOffsets.hitGroup * sbtEntrySize, sbtEntrySize,
// this->shaderBindingTableBuffer->GetBufferHandle(), 0, 0,
// this->swapChainExtent.width, this->swapChainExtent.height, 1);
// }
// TransitionImageLayout(frame.commandBuffer,
// frame.raytracingTexture->GetHandle(), this->swapChainImageFormat,
// vk::ImageLayout::eGeneral, vk::ImageLayout::eShaderReadOnlyOptimal,
// vk::PipelineStageFlagBits::eRayTracingShaderNV, vk::PipelineStageFlagBits::eAllGraphics);
TransitionImageLayout(frame.commandBuffer,
frame.swapChainImage, this->swapChainImageFormat,
vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal,
vk::PipelineStageFlagBits::eRayTracingShaderNV, vk::PipelineStageFlagBits::eAllGraphics);
if (this->IsOk())
{
frame.commandBuffer.bindPipeline(vk::PipelineBindPoint::eRayTracingNV, this->raytracingStep.pipeline);
frame.commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingNV,
this->raytracingStep.pipelineLayout,
0, frame.raytracingDescriptorSet,
{});
TransitionImageLayout(frame.commandBuffer,
frame.raytracingTexture->GetHandle(), this->swapChainImageFormat,
vk::ImageLayout::eUndefined, vk::ImageLayout::eGeneral,
vk::PipelineStageFlagBits::eTopOfPipe,
vk::PipelineStageFlagBits::eRayTracingShaderNV);
frame.ubo->ScheduleTransfer(frame.commandBuffer);
frame.commandBuffer.pushConstants(this->raytracingStep.pipelineLayout,
vk::ShaderStageFlagBits::eRaygenNV | vk::ShaderStageFlagBits::eClosestHitNV,
0, sizeof(this->pushConstant),
&this->pushConstant);
auto sbtEntrySize = this->physicalDeviceInfo.GetRaytracingProperties().shaderGroupBaseAlignment;
frame.commandBuffer.traceRaysNV(this->shaderBindingTableBuffer->GetBufferHandle(), this->shaderOffsets.rayGen * sbtEntrySize,
this->shaderBindingTableBuffer->GetBufferHandle(), this->shaderOffsets.miss * sbtEntrySize, sbtEntrySize,
this->shaderBindingTableBuffer->GetBufferHandle(), this->shaderOffsets.hitGroup * sbtEntrySize, sbtEntrySize,
this->shaderBindingTableBuffer->GetBufferHandle(), 0, 0,
this->swapChainExtent.width, this->swapChainExtent.height, 1);
}
if (this->IsOk())
{
// vk::RenderPassBeginInfo ppBeginInfo;
// ppBeginInfo.renderPass = this->rasterizationStep.renderPass;
// ppBeginInfo.framebuffer = frame.swapChainFramebufferForRasterizationRenderPass;
// ppBeginInfo.renderArea.offset = {0, 0};
// ppBeginInfo.renderArea.extent = this->swapChainExtent;
// frame.commandBuffer.beginRenderPass(ppBeginInfo, vk::SubpassContents::eInline);
// frame.commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, this->rasterizationStep.pipeline);
// frame.commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
// this->rasterizationStep.pipelineLayout,
// 0, frame.rasterizationDescriptorSet, {});
// frame.commandBuffer.draw(3, 1, 0, 0);
// frame.commandBuffer.endRenderPass();
vk::RenderPassBeginInfo ppBeginInfo;
ppBeginInfo.renderPass = this->rasterizationStep.renderPass;
ppBeginInfo.framebuffer = frame.swapChainFramebufferForRasterizationRenderPass;
ppBeginInfo.renderArea.offset = {0, 0};
ppBeginInfo.renderArea.extent = this->swapChainExtent;
frame.commandBuffer.beginRenderPass(ppBeginInfo, vk::SubpassContents::eInline);
frame.commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, this->rasterizationStep.pipeline);
frame.commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
this->rasterizationStep.pipelineLayout,
0, frame.rasterizationDescriptorSet, {});
frame.commandBuffer.draw(3, 1, 0, 0);
frame.commandBuffer.endRenderPass();
}
else
{
TransitionImageLayout(frame.commandBuffer,
frame.swapChainImage, this->swapChainImageFormat,
vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal,
vk::PipelineStageFlagBits::eTopOfPipe,
vk::PipelineStageFlagBits::eFragmentShader);
}
if (this->imGui.show)
......
#include "Util/PerlinGenerator.hpp"
#include "glm/glm.hpp"
#include <cmath>
#include <array>
namespace
{
double interpolate(double a, double b, double weight)
{
// TODO kann optimiert werden
auto trueWeight = 6.0 * std::pow(weight, 5.0) - 15.0 * std::pow(weight, 4.0) + 10.0 * std::pow(weight, 3.0);
return trueWeight * a + (1 - trueWeight) * b;
}
} // namespace
double lib::PerlinGenerator::perlin(double x, double y) const
{
// glm::vec2 point{std::fmod(x, 1.f), std::fmod(y, 1.f)};
// std::array<glm::vec2, 4> gradients;
// for (auto &gradient : gradients)
// {
// gradient.x = this->rnd(this->engine);
// gradient.y = this->rnd(this->engine);
// }
// static constexpr std::array<glm::vec2, 4> corners = {
// glm::vec2{0.f, 0.f},
// glm::vec2{0.f, 1.f},
// glm::vec2{1.f, 1.f},
// glm::vec2{1.f, 0.f},
// };
// std::array<float, 4> dotValues;
// for (size_t i = 0; i < dotValues.size(); i++)
// {
// dotValues[i] = glm::dot(point - corners[i], gradients[i]);
// }
// return interpolate(interpolate(dotValues[0], dotValues[1], point.x),
// interpolate(dotValues[2], dotValues[3], point.x),
// point.y);
return 0.0;
}
lib::PerlinGenerator::PerlinGenerator(uint32_t w, uint32_t h, uint32_t seed)
: width(w),
height(h),
engine(seed),
rnd()
{
}
uint32_t lib::PerlinGenerator::operator()(uint32_t x, uint32_t y) const
{
auto perlin = this->perlin(static_cast<double>(x) / this->width,
static_cast<double>(y) / this->height);
auto value = static_cast<uint8_t>(perlin * 255);
return uint32_t{0xff} |
static_cast<uint32_t>(value) << 8 |
static_cast<uint32_t>(value) << 16 |
static_cast<uint32_t>(value) << 24;
}
\ No newline at end of file