diff --git a/aggregated-graphics-samples.sln b/aggregated-graphics-samples.sln index f96bcb9..5e0314b 100644 --- a/aggregated-graphics-samples.sln +++ b/aggregated-graphics-samples.sln @@ -58,6 +58,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shadowmapping-poisson-stabl {67B01ECD-2613-4FA0-84F7-87F5BCF40EB1} = {67B01ECD-2613-4FA0-84F7-87F5BCF40EB1} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "normalmapping", "normalmapping\normalmapping.vcxproj", "{0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}" + ProjectSection(ProjectDependencies) = postProject + {8D9D4A3E-439A-4210-8879-259B20D992CA} = {8D9D4A3E-439A-4210-8879-259B20D992CA} + {51CC36B3-921E-4853-ACD5-CB6CBC27FBA1} = {51CC36B3-921E-4853-ACD5-CB6CBC27FBA1} + {67B01ECD-2613-4FA0-84F7-87F5BCF40EB1} = {67B01ECD-2613-4FA0-84F7-87F5BCF40EB1} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -138,6 +145,14 @@ Global {DD90A343-7897-442B-9D19-7FF0676E80EB}.Release|x64.Build.0 = Release|x64 {DD90A343-7897-442B-9D19-7FF0676E80EB}.Release|x86.ActiveCfg = Release|Win32 {DD90A343-7897-442B-9D19-7FF0676E80EB}.Release|x86.Build.0 = Release|Win32 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}.Debug|x64.ActiveCfg = Debug|x64 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}.Debug|x64.Build.0 = Debug|x64 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}.Debug|x86.ActiveCfg = Debug|Win32 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}.Debug|x86.Build.0 = Debug|Win32 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}.Release|x64.ActiveCfg = Release|x64 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}.Release|x64.Build.0 = Release|x64 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}.Release|x86.ActiveCfg = Release|Win32 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/assets/textures/steelplate1_normal-dx.dds b/assets/textures/steelplate1_normal-dx.dds new file mode 100644 index 0000000..52b9f97 Binary files /dev/null and b/assets/textures/steelplate1_normal-dx.dds differ diff --git a/framework/arcball.h b/framework/arcball.h index b0e7116..1acb038 100644 --- a/framework/arcball.h +++ b/framework/arcball.h @@ -1,5 +1,6 @@ #pragma once #include "core/noncopyable.h" +#include "core/aligned.h" #include "rapid/rapid.h" /* @@ -11,7 +12,7 @@ https://www.talisman.org/~erlkonig/misc/shoemake92-arcball.pdf */ -class Arcball : public core::NonCopyable +class Arcball : public core::Aligned<16>, public core::NonCopyable { public: Arcball(const rapid::vector2& center, float radius, diff --git a/framework/framework.vcxproj b/framework/framework.vcxproj index 2aaa181..ea1d494 100644 --- a/framework/framework.vcxproj +++ b/framework/framework.vcxproj @@ -144,6 +144,7 @@ + diff --git a/framework/framework.vcxproj.filters b/framework/framework.vcxproj.filters index cc3fa78..a127b09 100644 --- a/framework/framework.vcxproj.filters +++ b/framework/framework.vcxproj.filters @@ -99,6 +99,9 @@ Resource Files + + Resource Files + diff --git a/framework/graphicsApp.cpp b/framework/graphicsApp.cpp index 5fc3fb2..5945b57 100644 --- a/framework/graphicsApp.cpp +++ b/framework/graphicsApp.cpp @@ -21,9 +21,23 @@ GraphicsApp::GraphicsApp(const AppEntry& entry, const core::tstring& caption, ui magma::descriptors::CombinedImageSampler(8) })); + arcball = std::shared_ptr(new Trackball(rapid::vector2(width/2.f, height/2.f), 300.f, false)); timer = std::make_unique(); } +void GraphicsApp::onMouseMove(int x, int y) +{ + arcball->rotate(rapid::vector2((float)x, height - (float)y)); +} + +void GraphicsApp::onMouseLButton(bool down, int x, int y) +{ + if (down) + arcball->touch(rapid::vector2((float)x, height - (float)y)); + else + arcball->release(); +} + void GraphicsApp::createMultisampleFramebuffer(VkFormat colorFormat) { const VkFormat depthFormat = utilities::getSupportedDepthFormat(physicalDevice, false, true); @@ -216,6 +230,8 @@ std::shared_ptr GraphicsApp::createFullscreenPipeline(c void GraphicsApp::updateViewProjTransforms() { + viewProj->updateView(); + viewProj->updateProjection(); magma::helpers::mapScoped(viewProjTransforms, [this](auto *transforms) { @@ -252,6 +268,8 @@ void GraphicsApp::updateObjectTransforms(const std::vectorupdateView(); + lightViewProj->updateProjection(); magma::helpers::mapScoped(lightSource, [this](auto *light) { diff --git a/framework/graphicsApp.h b/framework/graphicsApp.h index c5fe293..a4db565 100644 --- a/framework/graphicsApp.h +++ b/framework/graphicsApp.h @@ -3,6 +3,7 @@ #include "core/alignedAllocator.h" #include "common.h" #include "viewProjection.h" +#include "arcball.h" #include "timer.h" class GraphicsApp : public VulkanApp @@ -10,6 +11,8 @@ class GraphicsApp : public VulkanApp public: GraphicsApp(const AppEntry& entry, const core::tstring& caption, uint32_t width, uint32_t height, bool sRGB); + virtual void onMouseMove(int x, int y) override; + virtual void onMouseLButton(bool down, int x, int y) override; protected: virtual void createMultisampleFramebuffer(VkFormat colorFormat); @@ -71,5 +74,6 @@ class GraphicsApp : public VulkanApp std::shared_ptr trilinearClampToEdge; std::shared_ptr anisotropicClampToEdge; + std::shared_ptr arcball; std::unique_ptr timer; }; diff --git a/framework/shaders/common/reconstruct.h b/framework/shaders/common/reconstruct.h new file mode 100644 index 0000000..ad2c5c7 --- /dev/null +++ b/framework/shaders/common/reconstruct.h @@ -0,0 +1,7 @@ +vec3 reconstructNormal(vec2 xy) +{ + vec3 n; + n.xy = xy; + n.z = sqrt(1. - dot(xy, xy)); + return normalize(n); +} diff --git a/framework/textureLoader.cpp b/framework/textureLoader.cpp index 2e3e3b5..29c08cf 100644 --- a/framework/textureLoader.cpp +++ b/framework/textureLoader.cpp @@ -19,38 +19,106 @@ static VkFormat blockCompressedFormat(const gliml::context& ctx) } } +static void loadBC5SignedNorm(const gliml::dds_header *hdr, VkExtent2D& extent, magma::Image::MipmapLayout& mipOffsets, VkDeviceSize& baseMipOffset) +{ + const unsigned char* dataBytePtr = (const unsigned char *)hdr; + dataBytePtr += sizeof(gliml::dds_header); + + constexpr int MaxNumMipmaps = 16; + struct Face { + int numMipmaps; + struct mipmap { + int width; + int height; + int depth; + int size; + const void* data; + } mipmaps[MaxNumMipmaps]; + } face; + + face.numMipmaps = (hdr->dwMipMapCount == 0) ? 1 : hdr->dwMipMapCount; + // For each mipmap + for (int mipIndex = 0; mipIndex < face.numMipmaps; mipIndex++) + { + Face::mipmap& curMip = face.mipmaps[mipIndex]; + // mipmap dimensions + int w = hdr->dwWidth >> mipIndex; + if (w <= 0) w = 1; + int h = hdr->dwHeight >> mipIndex; + if (h <= 0) h = 1; + int d = hdr->dwDepth >> mipIndex; + if (d <= 0) d = 1; + curMip.width = w; + curMip.height = h; + curMip.depth = d; + // Mipmap byte size + constexpr int bytesPerElement = 16; + curMip.size = ((w + 3) / 4) * ((h + 3) / 4) * d * bytesPerElement; + // Set and advance surface data pointer + curMip.data = dataBytePtr; + dataBytePtr += curMip.size; + } + + extent.width = static_cast(face.mipmaps[0].width); + extent.height = static_cast(face.mipmaps[0].height); + for (int level = 1; level < face.numMipmaps; ++level) + { // Compute relative offset + const intptr_t mipOffset = (const uint8_t *)face.mipmaps[level].data - (const uint8_t *)face.mipmaps[level - 1].data; + mipOffsets.push_back(mipOffset); + } + // Skip DDS header + baseMipOffset = (const uint8_t *)face.mipmaps[0].data - (const uint8_t *)hdr; +} + std::shared_ptr loadDxtTexture(std::shared_ptr cmdCopy, const std::string& filename) { std::ifstream file("../assets/textures/" + filename, std::ios::in | std::ios::binary | std::ios::ate); if (!file.is_open()) - throw std::runtime_error("failed to open file ../assets/textures/\"" + filename + "\""); + throw std::runtime_error("failed to open file \"../assets/textures/" + filename + "\""); const std::streamoff size = file.tellg(); file.seekg(0, std::ios::beg); gliml::context ctx; + VkFormat format; + VkExtent2D extent; VkDeviceSize baseMipOffset = 0; + magma::Image::MipmapLayout mipOffsets(1, 0); std::shared_ptr buffer = std::make_shared(cmdCopy->getDevice(), size); magma::helpers::mapScoped(buffer, [&](uint8_t *data) { // Read data to buffer file.read(reinterpret_cast(data), size); file.close(); ctx.enable_dxt(true); - if (!ctx.load(data, static_cast(size))) + if (ctx.load(data, static_cast(size))) + { // Setup texture data description + format = blockCompressedFormat(ctx); + extent.width = static_cast(ctx.image_width(0, 0)); + extent.height = static_cast(ctx.image_height(0, 0)); + for (int level = 1; level < ctx.num_mipmaps(0); ++level) + { // Compute relative offset + const intptr_t mipOffset = (const uint8_t *)ctx.image_data(0, level) - (const uint8_t *)ctx.image_data(0, level - 1); + mipOffsets.push_back(mipOffset); + } + // Skip DDS header + baseMipOffset = (const uint8_t *)ctx.image_data(0, 0) - data; + } + else if (ctx.error() == GLIML_ERROR_INVALID_COMPRESSED_FORMAT) + { // Not supported, proceed ourself + const gliml::dds_header *hdr = (const gliml::dds_header *)data; + switch (hdr->ddspf.dwFourCC) + { + case MAKEFOURCC('B', 'C', '5', 'S'): // ATI1 + format = VK_FORMAT_BC5_SNORM_BLOCK; + loadBC5SignedNorm(hdr, extent, mipOffsets, baseMipOffset); + break; + default: + throw std::runtime_error("unknown compressed format"); + } + } + else + { throw std::runtime_error("failed to load DDS texture"); - // Skip DDS header - baseMipOffset = reinterpret_cast(ctx.image_data(0, 0)) - data; + } }); - // Setup texture data description - const VkFormat format = blockCompressedFormat(ctx); - const VkExtent2D extent = { - static_cast(ctx.image_width(0, 0)), - static_cast(ctx.image_height(0, 0)) - }; - magma::Image::MipmapLayout mipOffsets(1, 0); - for (int level = 1; level < ctx.num_mipmaps(0); ++level) - { // Compute relative offset - const intptr_t mipOffset = (const uint8_t *)ctx.image_data(0, level) - (const uint8_t *)ctx.image_data(0, level - 1); - mipOffsets.push_back(mipOffset); - } // Upload texture data from buffer magma::Image::CopyLayout bufferLayout{baseMipOffset, 0, 0}; std::shared_ptr image = std::make_shared(cmdCopy, diff --git a/framework/viewProjection.cpp b/framework/viewProjection.cpp index 154b8db..a638b2d 100644 --- a/framework/viewProjection.cpp +++ b/framework/viewProjection.cpp @@ -2,7 +2,7 @@ ViewProjection::ViewProjection(bool lhs) noexcept: lhs(lhs), - eyePos(0.f, 0.f, 0.f), + eyePos(0.f, 0.f, -1.f), focusPos(0.f, 0.f, 0.f) {} diff --git a/framework/viewProjection.h b/framework/viewProjection.h index e59c424..5ed39c4 100644 --- a/framework/viewProjection.h +++ b/framework/viewProjection.h @@ -39,7 +39,7 @@ class ViewProjection : public core::Aligned<16>, public core::NonCopyable bool lhs; rapid::float3 eyePos; rapid::float3 focusPos; - float fieldOfView = 0.f; + float fieldOfView = 45.f; float aspectRatio = 1.f; float zNear = 0.1f; float zFar = 100.f; diff --git a/normalmapping/normalmapping.cpp b/normalmapping/normalmapping.cpp new file mode 100644 index 0000000..6dc81a6 --- /dev/null +++ b/normalmapping/normalmapping.cpp @@ -0,0 +1,199 @@ +#include "graphicsApp.h" +#include "textureLoader.h" + +#include "quadric/include/torus.h" +#include "quadric/include/sphere.h" + +class NormalMapping : public GraphicsApp +{ + struct Constants + { + VkBool32 showNormals; + }; + + std::unique_ptr torus; + std::unique_ptr sphere; + std::shared_ptr normalMap; + std::shared_ptr bumpPipeline; + std::shared_ptr spherePipeline; + DescriptorSet bumpDescriptor; + DescriptorSet fillDescriptor; + + Constants constants = {false}; + +public: + explicit NormalMapping(const AppEntry& entry): + GraphicsApp(entry, TEXT("Normal mapping"), 1280, 720, true) + { + setupViewProjection(); + createTransformBuffer(2); + createMeshObjects(); + loadTexture(); + setupDescriptorSets(); + setupGraphicsPipelines(); + + renderScene(drawCmdBuffer); + blit(msaaFramebuffer->getColorView(), FrontBuffer); + blit(msaaFramebuffer->getColorView(), BackBuffer); + } + + virtual void render(uint32_t bufferIndex) override + { + updateTransforms(); + submitCommandBuffers(bufferIndex); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); // Cap fps + } + + virtual void onKeyDown(char key, int repeat, uint32_t flags) override + { + constexpr float step = 0.1f; + switch (key) + { + case AppKey::Left: + lightViewProj->translate(-step, 0.f, 0.f); + break; + case AppKey::Right: + lightViewProj->translate(step, 0.f, 0.f); + break; + case AppKey::Up: + lightViewProj->translate(0.f, step, 0.f); + break; + case AppKey::Down: + lightViewProj->translate(0.f, -step, 0.f); + break; + case AppKey::PgUp: + lightViewProj->translate(0.f, 0.f, step); + break; + case AppKey::PgDn: + lightViewProj->translate(0.f, 0.f, -step); + break; + case AppKey::Space: + constants.showNormals = !constants.showNormals; + setupGraphicsPipelines(); + renderScene(drawCmdBuffer); + break; + } + updateTransforms(); + updateLightSource(); + VulkanApp::onKeyDown(key, repeat, flags); + } + + virtual void onMouseWheel(float distance) override + { + float step = distance * 0.2f; + float z = viewProj->getPosition().z; + if ((z + step < -2.5f) && (z + step > -15.f)) + viewProj->translate(0.f, 0.f, step); + updateViewProjTransforms(); + } + + void setupViewProjection() + { + viewProj = std::make_unique(); + viewProj->setPosition(0.f, 0.f, -4.f); + viewProj->setFocus(0.f, 0.f, 0.f); + viewProj->setFieldOfView(60.f); + viewProj->setNearZ(1.f); + viewProj->setFarZ(100.f); + viewProj->setAspectRatio(width / (float)height); + viewProj->updateView(); + viewProj->updateProjection(); + + lightViewProj = std::make_unique(); + lightViewProj->setPosition(-1., 1.f, -1.f); + + updateViewProjTransforms(); + } + + void updateTransforms() + { + rapid::matrix lightTransform = rapid::translation(rapid::vector3(lightViewProj->getPosition())); + const std::vector> transforms = { + arcball->transform(), + lightTransform + }; + updateObjectTransforms(transforms); + } + + void createMeshObjects() + { + torus = std::make_unique(0.4f, 1.0f, 64, 128, true, cmdCopyBuf); + sphere = std::make_unique(0.02f, 8, 8, true, cmdCopyBuf); + } + + void loadTexture() + { // https://freepbr.com/materials/steel-plate1/ + normalMap = loadDxtTexture(cmdCopyImg, "steelplate1_normal-dx.dds"); + } + + void setupDescriptorSets() + { + bumpDescriptor.layout = std::shared_ptr(new magma::DescriptorSetLayout(device, + { + magma::bindings::VertexFragmentStageBinding(0, magma::descriptors::DynamicUniformBuffer(1)), + magma::bindings::FragmentStageBinding(1, magma::descriptors::UniformBuffer(1)), + magma::bindings::FragmentStageBinding(2, magma::descriptors::UniformBuffer(1)), + magma::bindings::FragmentStageBinding(3, magma::descriptors::CombinedImageSampler(1)) + })); + bumpDescriptor.set = descriptorPool->allocateDescriptorSet(bumpDescriptor.layout); + bumpDescriptor.set->update(0, transforms); + bumpDescriptor.set->update(1, viewProjTransforms); + bumpDescriptor.set->update(2, lightSource); + bumpDescriptor.set->update(3, normalMap, anisotropicClampToEdge); + + fillDescriptor.layout = std::shared_ptr(new magma::DescriptorSetLayout(device, + { + magma::bindings::VertexStageBinding(0, magma::descriptors::DynamicUniformBuffer(1)) + })); + fillDescriptor.set = descriptorPool->allocateDescriptorSet(fillDescriptor.layout); + fillDescriptor.set->update(0, transforms); + } + + void setupGraphicsPipelines() + { + std::shared_ptr specialization(new magma::Specialization(constants, + { + magma::SpecializationEntry(0, &Constants::showNormals) + } + )); + bumpPipeline = createCommonSpecializedPipeline( + "transform.o", "bump.o", + std::move(specialization), + torus->getVertexInput(), + bumpDescriptor.layout); + if (!spherePipeline) + { + spherePipeline = createCommonPipeline( + "transform.o", "fill.o", + sphere->getVertexInput(), + fillDescriptor.layout); + } + } + + void renderScene(std::shared_ptr cmdBuffer) + { + cmdBuffer->begin(); + { + cmdBuffer->beginRenderPass(msaaFramebuffer->getRenderPass(), msaaFramebuffer->getFramebuffer(), + { + magma::ClearColor(0.1f, 0.243f, 0.448f, 1.f), + magma::clears::depthOne + }); + { + cmdBuffer->bindPipeline(bumpPipeline); + cmdBuffer->bindDescriptorSet(bumpPipeline, bumpDescriptor.set, transforms->getDynamicOffset(0)); + torus->draw(cmdBuffer); + cmdBuffer->bindPipeline(spherePipeline); + cmdBuffer->bindDescriptorSet(spherePipeline, fillDescriptor.set, transforms->getDynamicOffset(1)); + sphere->draw(cmdBuffer); + } + cmdBuffer->endRenderPass(); + } + cmdBuffer->end(); + } +}; + +std::unique_ptr appFactory(const AppEntry& entry) +{ + return std::unique_ptr(new NormalMapping(entry)); +} diff --git a/normalmapping/normalmapping.vcxproj b/normalmapping/normalmapping.vcxproj new file mode 100644 index 0000000..0fea745 --- /dev/null +++ b/normalmapping/normalmapping.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {0CC7CBF2-BFCD-477F-A639-5CAB35FCBA6C} + normalmapping + 10.0.16299.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + $(VK_SDK_PATH)\Include;..\third-party;..\framework + NDEBUG;VK_USE_PLATFORM_WIN32_KHR;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + Windows + $(VK_SDK_PATH)\Lib;..\x64\Release + vulkan-1.lib;magma.lib;quadric.lib;framework.lib;Shcore.lib;%(AdditionalDependencies) + + + + + Level3 + Disabled + true + true + $(VK_SDK_PATH)\Include;..\third-party;..\framework + _DEBUG;VK_USE_PLATFORM_WIN32_KHR;%(PreprocessorDefinitions) + + + Windows + $(VK_SDK_PATH)\Lib32;..\Debug + vulkan-1.lib;magma.lib;quadric.lib;framework.lib;Shcore.lib;%(AdditionalDependencies) + + + + + Level3 + Disabled + true + true + $(VK_SDK_PATH)\Include;..\third-party;..\framework + _DEBUG;VK_USE_PLATFORM_WIN32_KHR;%(PreprocessorDefinitions) + + + Windows + $(VK_SDK_PATH)\Lib;..\x64\Debug + vulkan-1.lib;magma.lib;quadric.lib;framework.lib;Shcore.lib;%(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + true + true + $(VK_SDK_PATH)\Include;..\third-party;..\framework + NDEBUG;VK_USE_PLATFORM_WIN32_KHR;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + Windows + $(VK_SDK_PATH)\Lib32;..\Release + vulkan-1.lib;magma.lib;quadric.lib;framework.lib;Shcore.lib;%(AdditionalDependencies) + + + + + + + + Document + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + %(Filename).o + %(Filename).o + %(Filename).o + %(Filename).o + + + Document + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + %(Filename).o + %(Filename).o + %(Filename).o + %(Filename).o + + + + + Document + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + $(VK_SDK_PATH)\Bin32\glslangValidator.exe -V %(FullPath) -I..\framework\shaders -o %(Filename).o + %(Filename).o + %(Filename).o + %(Filename).o + %(Filename).o + + + + + + \ No newline at end of file diff --git a/normalmapping/normalmapping.vcxproj.filters b/normalmapping/normalmapping.vcxproj.filters new file mode 100644 index 0000000..98c952b --- /dev/null +++ b/normalmapping/normalmapping.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/normalmapping/shaders/bump.frag b/normalmapping/shaders/bump.frag new file mode 100644 index 0000000..2271bd1 --- /dev/null +++ b/normalmapping/shaders/bump.frag @@ -0,0 +1,45 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#include "common/transforms.h" +#include "common/reconstruct.h" +#include "common/cotangentFrame.h" +#include "common/sRGB.h" + +layout(constant_id = 0) const bool c_showNormals = false; + +layout(binding = 2) uniform Light +{ + vec3 viewPos; +} light; + +layout(binding = 3) uniform sampler2D normalMap; + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 viewPos; +layout(location = 2) in vec3 normal; +layout(location = 3) in vec2 texCoord; + +layout(location = 0) out vec3 oColor; + +void main() +{ + vec2 nxy = texture(normalMap, texCoord).xy; + + // compute per-pixel cotangent frame + vec3 N = normalize(normal); + mat3 TBN = cotangentFrame(N, position, texCoord); + + // transform from texture space to object space + vec3 normal = TBN * reconstructNormal(nxy); + // transform from object space to view space + vec3 viewNormal = mat3(normalMatrix) * normal; + + vec3 n = normalize(viewNormal); + vec3 l = normalize(light.viewPos - viewPos); + float NdL = dot(n, l); + + if (c_showNormals) + oColor = linear(normal * .5 + .5); + else + oColor = vec3(max(NdL, 0.)); +} diff --git a/normalmapping/shaders/fill.frag b/normalmapping/shaders/fill.frag new file mode 100644 index 0000000..dd3e227 --- /dev/null +++ b/normalmapping/shaders/fill.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location = 0) out vec3 oColor; + +void main() +{ + oColor = vec3(1, 1, 0); +} diff --git a/normalmapping/shaders/transform.vert b/normalmapping/shaders/transform.vert new file mode 100644 index 0000000..bee454a --- /dev/null +++ b/normalmapping/shaders/transform.vert @@ -0,0 +1,25 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#include "common/transforms.h" + +layout(location = 0) in vec4 position; +layout(location = 1) in vec3 normal; +layout(location = 2) in vec2 texCoord; + +layout(location = 0) out vec3 oPos; +layout(location = 1) out vec3 oViewPos; +layout(location = 2) out vec3 oNormal; +layout(location = 3) out vec2 oTexCoord; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() +{ + oPos = position.xyz; + oViewPos = (worldView * position).xyz; + oNormal = normal; + oTexCoord = texCoord; + gl_Position = worldViewProj * position; +} diff --git a/screenshots/normalmapping.jpg b/screenshots/normalmapping.jpg new file mode 100644 index 0000000..bc2a384 Binary files /dev/null and b/screenshots/normalmapping.jpg differ