From c11663743d6012f69c80b47b0df957e03307cdf8 Mon Sep 17 00:00:00 2001 From: mogemimi Date: Sat, 2 Nov 2019 15:27:17 +0900 Subject: [PATCH] Add built-in BasicEffect --- build/pomdog.xcodeproj/project.pbxproj | 8 + build/pomdog/CMakeLists.txt | 2 + examples/FeatureShowcase/CMakeLists.txt | 3 + .../BasicEffectTest/BasicEffectTest.cpp | 286 ++++++++++++++++++ .../BasicEffectTest/BasicEffectTest.hpp | 37 +++ .../Source/FeatureShowcaseGame.cpp | 5 + .../Experimental/Graphics/BasicEffect.hpp | 104 +++++++ src/Experimental/Graphics/BasicEffect.cpp | 97 ++++++ .../GLSL.Embedded/BasicEffect_PS.inc.hpp | 76 +++++ .../GLSL.Embedded/BasicEffect_VS.inc.hpp | 54 ++++ .../Graphics/Shaders/GLSL/BasicEffect_PS.glsl | 106 +++++++ .../Graphics/Shaders/GLSL/BasicEffect_VS.glsl | 68 +++++ .../Shaders/HLSL.Embedded/BasicEffect.inc.hpp | 111 +++++++ .../Graphics/Shaders/HLSL/BasicEffect.hlsl | 149 +++++++++ .../Metal.Embedded/BasicEffect.inc.hpp | 137 +++++++++ .../Graphics/Shaders/Metal/BasicEffect.metal | 183 +++++++++++ 16 files changed, 1426 insertions(+) create mode 100644 examples/FeatureShowcase/Source/BasicEffectTest/BasicEffectTest.cpp create mode 100644 examples/FeatureShowcase/Source/BasicEffectTest/BasicEffectTest.hpp create mode 100644 include/Pomdog/Experimental/Graphics/BasicEffect.hpp create mode 100644 src/Experimental/Graphics/BasicEffect.cpp create mode 100644 src/Experimental/Graphics/Shaders/GLSL.Embedded/BasicEffect_PS.inc.hpp create mode 100644 src/Experimental/Graphics/Shaders/GLSL.Embedded/BasicEffect_VS.inc.hpp create mode 100644 src/Experimental/Graphics/Shaders/GLSL/BasicEffect_PS.glsl create mode 100644 src/Experimental/Graphics/Shaders/GLSL/BasicEffect_VS.glsl create mode 100644 src/Experimental/Graphics/Shaders/HLSL.Embedded/BasicEffect.inc.hpp create mode 100644 src/Experimental/Graphics/Shaders/HLSL/BasicEffect.hlsl create mode 100644 src/Experimental/Graphics/Shaders/Metal.Embedded/BasicEffect.inc.hpp create mode 100644 src/Experimental/Graphics/Shaders/Metal/BasicEffect.metal diff --git a/build/pomdog.xcodeproj/project.pbxproj b/build/pomdog.xcodeproj/project.pbxproj index b45e036a0..875cdfb67 100644 --- a/build/pomdog.xcodeproj/project.pbxproj +++ b/build/pomdog.xcodeproj/project.pbxproj @@ -293,6 +293,8 @@ D719A53F23497EB600C1868B /* PNM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D719A53923497EB600C1868B /* PNM.cpp */; }; D719A55F2349868200C1868B /* TrueTypeFontLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D719A55E2349868200C1868B /* TrueTypeFontLoader.cpp */; }; D719A5602349868200C1868B /* TrueTypeFontLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D719A55E2349868200C1868B /* TrueTypeFontLoader.cpp */; }; + D7255BA6236D59AB00B8E4A0 /* BasicEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D7255BA5236D59AB00B8E4A0 /* BasicEffect.cpp */; }; + D7255BA7236D59AB00B8E4A0 /* BasicEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D7255BA5236D59AB00B8E4A0 /* BasicEffect.cpp */; }; D7334DC8228AC80200232225 /* PolylineBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D7334DC6228AC80200232225 /* PolylineBatch.cpp */; }; D7334DC9228AC80200232225 /* PolylineBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D7334DC6228AC80200232225 /* PolylineBatch.cpp */; }; D7334DCA228AC80200232225 /* PrimitiveBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D7334DC7228AC80200232225 /* PrimitiveBatch.cpp */; }; @@ -883,6 +885,8 @@ D719A55C2349864500C1868B /* SpriteFont.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SpriteFont.hpp; sourceTree = ""; }; D719A55D2349864500C1868B /* SpriteFontLoader.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SpriteFontLoader.hpp; sourceTree = ""; }; D719A55E2349868200C1868B /* TrueTypeFontLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrueTypeFontLoader.cpp; sourceTree = ""; }; + D7255BA4236D598C00B8E4A0 /* BasicEffect.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = BasicEffect.hpp; sourceTree = ""; }; + D7255BA5236D59AB00B8E4A0 /* BasicEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BasicEffect.cpp; sourceTree = ""; }; D7334DC6228AC80200232225 /* PolylineBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PolylineBatch.cpp; sourceTree = ""; }; D7334DC7228AC80200232225 /* PrimitiveBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrimitiveBatch.cpp; sourceTree = ""; }; D7334DCC228AC81F00232225 /* SpriteLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpriteLine.cpp; sourceTree = ""; }; @@ -1739,6 +1743,7 @@ A9FC99031DC3D86C00C78D63 /* Graphics */ = { isa = PBXGroup; children = ( + D7255BA5236D59AB00B8E4A0 /* BasicEffect.cpp */, A98F5917211B2B89002960C1 /* LineBatch.cpp */, A98F5918211B2B89002960C1 /* PolygonShapeBuilder.cpp */, D7334DC6228AC80200232225 /* PolylineBatch.cpp */, @@ -1757,6 +1762,7 @@ A9FC99081DC3D8E100C78D63 /* Graphics */ = { isa = PBXGroup; children = ( + D7255BA4236D598C00B8E4A0 /* BasicEffect.hpp */, D719A55A2349864500C1868B /* FontGlyph.hpp */, D719A5572349864500C1868B /* LineBatch.hpp */, D719A5592349864500C1868B /* PolygonShapeBuilder.hpp */, @@ -2531,6 +2537,7 @@ A98F5926211B2B9A002960C1 /* SpriteFontLoader.cpp in Sources */, D7334E17228AEC1300232225 /* PostProcessCompositor.cpp in Sources */, 03F46EF697B70E1EE9B2533B /* CocoaWindowDelegate.mm in Sources */, + D7255BA6236D59AB00B8E4A0 /* BasicEffect.cpp in Sources */, 2A5B52A528596019ABF40617 /* GameHostCocoa.mm in Sources */, E6DAE91B1C77C1736DB66CD7 /* GameWindowCocoa.mm in Sources */, E9DF3779DAEBF65FC42EA8CF /* KeyboardCocoa.cpp in Sources */, @@ -2701,6 +2708,7 @@ A98F5927211B2B9A002960C1 /* SpriteFontLoader.cpp in Sources */, D7334E18228AEC1300232225 /* PostProcessCompositor.cpp in Sources */, E8ACB94BC7A78F57A854EC2C /* CocoaWindowDelegate.mm in Sources */, + D7255BA7236D59AB00B8E4A0 /* BasicEffect.cpp in Sources */, 0AECD9F61617195B0205EFEF /* GameHostCocoa.mm in Sources */, 71E352760BC127DCF199C59B /* GameWindowCocoa.mm in Sources */, 94FC0ADEB7D7E0AE9084A692 /* KeyboardCocoa.cpp in Sources */, diff --git a/build/pomdog/CMakeLists.txt b/build/pomdog/CMakeLists.txt index dde154665..7cf82c685 100644 --- a/build/pomdog/CMakeLists.txt +++ b/build/pomdog/CMakeLists.txt @@ -368,6 +368,7 @@ set(POMDOG_SOURCES_CORE ) set(POMDOG_SOURCES_EXPERIMENTAL + ${POMDOG_DIR}/include/Pomdog/Experimental/Graphics/BasicEffect.hpp ${POMDOG_DIR}/include/Pomdog/Experimental/Graphics/FontGlyph.hpp ${POMDOG_DIR}/include/Pomdog/Experimental/Graphics/LineBatch.hpp ${POMDOG_DIR}/include/Pomdog/Experimental/Graphics/PolylineBatch.hpp @@ -441,6 +442,7 @@ set(POMDOG_SOURCES_EXPERIMENTAL ${POMDOG_DIR}/include/Pomdog/Experimental/TexturePacker/TextureAtlasLoader.hpp ${POMDOG_DIR}/include/Pomdog/Experimental/TexturePacker/TextureRegion.hpp ${POMDOG_DIR}/include/Pomdog/Experimental/Tween/EasingHelper.hpp + ${POMDOG_DIR}/src/Experimental/Graphics/BasicEffect.cpp ${POMDOG_DIR}/src/Experimental/Graphics/LineBatch.cpp ${POMDOG_DIR}/src/Experimental/Graphics/PolylineBatch.cpp ${POMDOG_DIR}/src/Experimental/Graphics/PolygonShapeBuilder.cpp diff --git a/examples/FeatureShowcase/CMakeLists.txt b/examples/FeatureShowcase/CMakeLists.txt index e2af174b4..cf95a0966 100644 --- a/examples/FeatureShowcase/CMakeLists.txt +++ b/examples/FeatureShowcase/CMakeLists.txt @@ -13,6 +13,7 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release) source_group(Source REGULAR_EXPRESSION "Source/*") source_group(Source\\AudioClipTest REGULAR_EXPRESSION "Source/AudioClipTest/*") +source_group(Source\\BasicEffectTest REGULAR_EXPRESSION "Source/BasicEffectTest/*") source_group(Source\\Beam2DTest REGULAR_EXPRESSION "Source/Beam2DTest/*") source_group(Source\\GamepadTest REGULAR_EXPRESSION "Source/GamepadTest/*") source_group(Source\\HTTPClientTest REGULAR_EXPRESSION "Source/HTTPClientTest/*") @@ -44,6 +45,8 @@ add_executable(${PRODUCT_NAME} WIN32 Source/FeatureShowcaseGame.hpp Source/AudioClipTest/AudioClipTest.cpp Source/AudioClipTest/AudioClipTest.hpp + Source/BasicEffectTest/BasicEffectTest.cpp + Source/BasicEffectTest/BasicEffectTest.hpp Source/Beam2DTest/Beam2DTest.cpp Source/Beam2DTest/Beam2DTest.hpp Source/GamepadTest/GamepadTest.cpp diff --git a/examples/FeatureShowcase/Source/BasicEffectTest/BasicEffectTest.cpp b/examples/FeatureShowcase/Source/BasicEffectTest/BasicEffectTest.cpp new file mode 100644 index 000000000..946e8951a --- /dev/null +++ b/examples/FeatureShowcase/Source/BasicEffectTest/BasicEffectTest.cpp @@ -0,0 +1,286 @@ +#include "BasicEffectTest.hpp" +#include +#include + +namespace FeatureShowcase { + +BasicEffectTest::BasicEffectTest(const std::shared_ptr& gameHostIn) + : gameHost(gameHostIn) + , graphicsDevice(gameHostIn->GetGraphicsDevice()) + , commandQueue(gameHostIn->GetGraphicsCommandQueue()) +{ +} + +void BasicEffectTest::Initialize() +{ + auto assets = gameHost->GetAssetManager(); + auto clock = gameHost->GetClock(); + commandList = std::make_shared(*graphicsDevice); + + // NOTE: Load texture from image file + if (auto[res, err] = assets->Load("Textures/pomdog.png"); err != nullptr) { + Log::Verbose("failed to load texture: " + err->ToString()); + } + else { + texture = std::move(res); + } + + { + using VertexCombined = BasicEffect::VertexPositionNormalTexture; + + // NOTE: Create vertex buffer + std::array verticesCombo = {{ + // top + {Vector3{0.0f, 1.0f, 0.0f}, Vector3{0.0f, 1.0f, 0.0f}, Vector2{0.0f, 1.0f}}, + {Vector3{1.0f, 1.0f, 0.0f}, Vector3{0.0f, 1.0f, 0.0f}, Vector2{1.0f, 1.0f}}, + {Vector3{1.0f, 1.0f, 1.0f}, Vector3{0.0f, 1.0f, 0.0f}, Vector2{1.0f, 0.0f}}, + {Vector3{0.0f, 1.0f, 1.0f}, Vector3{0.0f, 1.0f, 0.0f}, Vector2{0.0f, 0.0f}}, + + // left + {Vector3{0.0f, 0.0f, 1.0f}, Vector3{-1.0f, 0.0f, 0.0f}, Vector2{0.0f, 1.0f}}, + {Vector3{0.0f, 0.0f, 0.0f}, Vector3{-1.0f, 0.0f, 0.0f}, Vector2{1.0f, 1.0f}}, + {Vector3{0.0f, 1.0f, 0.0f}, Vector3{-1.0f, 0.0f, 0.0f}, Vector2{1.0f, 0.0f}}, + {Vector3{0.0f, 1.0f, 1.0f}, Vector3{-1.0f, 0.0f, 0.0f}, Vector2{0.0f, 0.0f}}, + + // right + {Vector3{1.0f, 0.0f, 1.0f}, Vector3{1.0f, 0.0f, 0.0f}, Vector2{0.0f, 0.0f}}, + {Vector3{1.0f, 0.0f, 0.0f}, Vector3{1.0f, 0.0f, 0.0f}, Vector2{1.0f, 0.0f}}, + {Vector3{1.0f, 1.0f, 0.0f}, Vector3{1.0f, 0.0f, 0.0f}, Vector2{1.0f, 1.0f}}, + {Vector3{1.0f, 1.0f, 1.0f}, Vector3{1.0f, 0.0f, 0.0f}, Vector2{0.0f, 1.0f}}, + + // front + {Vector3{0.0f, 0.0f, 0.0f}, Vector3{0.0f, 0.0f, -1.0f}, Vector2{0.0f, 0.0f}}, + {Vector3{1.0f, 0.0f, 0.0f}, Vector3{0.0f, 0.0f, -1.0f}, Vector2{1.0f, 0.0f}}, + {Vector3{1.0f, 1.0f, 0.0f}, Vector3{0.0f, 0.0f, -1.0f}, Vector2{1.0f, 1.0f}}, + {Vector3{0.0f, 1.0f, 0.0f}, Vector3{0.0f, 0.0f, -1.0f}, Vector2{0.0f, 1.0f}}, + + // back + {Vector3{0.0f, 0.0f, 1.0f}, Vector3{0.0f, 0.0f, 1.0f}, Vector2{0.0f, 0.0f}}, + {Vector3{1.0f, 0.0f, 1.0f}, Vector3{0.0f, 0.0f, 1.0f}, Vector2{1.0f, 0.0f}}, + {Vector3{1.0f, 1.0f, 1.0f}, Vector3{0.0f, 0.0f, 1.0f}, Vector2{1.0f, 1.0f}}, + {Vector3{0.0f, 1.0f, 1.0f}, Vector3{0.0f, 0.0f, 1.0f}, Vector2{0.0f, 1.0f}}, + }}; + + vertexBuffer1 = std::make_shared( + graphicsDevice, + verticesCombo.data(), + verticesCombo.size(), + sizeof(VertexCombined), + BufferUsage::Immutable); + } + { + using VertexCombined = BasicEffect::VertexPositionColor; + + // NOTE: Create vertex buffer + std::array verticesCombo = {{ + // top + {Vector3{0.0f, 1.0f, 0.0f}, Color::White.ToVector4()}, + {Vector3{1.0f, 1.0f, 0.0f}, Color::White.ToVector4()}, + {Vector3{1.0f, 1.0f, 1.0f}, Color::White.ToVector4()}, + {Vector3{0.0f, 1.0f, 1.0f}, Color::White.ToVector4()}, + + // left + {Vector3{0.0f, 0.0f, 1.0f}, Color::Yellow.ToVector4()}, + {Vector3{0.0f, 0.0f, 0.0f}, Color::Yellow.ToVector4()}, + {Vector3{0.0f, 1.0f, 0.0f}, Color::Yellow.ToVector4()}, + {Vector3{0.0f, 1.0f, 1.0f}, Color::Yellow.ToVector4()}, + + // right + {Vector3{1.0f, 0.0f, 1.0f}, Color::Red.ToVector4()}, + {Vector3{1.0f, 0.0f, 0.0f}, Color::Red.ToVector4()}, + {Vector3{1.0f, 1.0f, 0.0f}, Color::Red.ToVector4()}, + {Vector3{1.0f, 1.0f, 1.0f}, Color::Red.ToVector4()}, + + // front + {Vector3{0.0f, 0.0f, 0.0f}, Color::Blue.ToVector4()}, + {Vector3{1.0f, 0.0f, 0.0f}, Color::Blue.ToVector4()}, + {Vector3{1.0f, 1.0f, 0.0f}, Color::Blue.ToVector4()}, + {Vector3{0.0f, 1.0f, 0.0f}, Color::Blue.ToVector4()}, + + // back + {Vector3{0.0f, 0.0f, 1.0f}, Color::Red.ToVector4()}, + {Vector3{1.0f, 0.0f, 1.0f}, Color::Yellow.ToVector4()}, + {Vector3{1.0f, 1.0f, 1.0f}, Color::Blue.ToVector4()}, + {Vector3{0.0f, 1.0f, 1.0f}, Color::White.ToVector4()}, + }}; + + vertexBuffer2 = std::make_shared( + graphicsDevice, + verticesCombo.data(), + verticesCombo.size(), + sizeof(VertexCombined), + BufferUsage::Immutable); + } + { + // NOTE: Create index buffer + std::array indices = {{ + 3, 1, 0, + 2, 1, 3, + + 7, 5, 4, + 6, 5, 7, + + 10, 8, 9, + 11, 8, 10, + + 15, 13, 12, + 14, 13, 15, + + 18, 16, 17, + 19, 16, 18, + }}; + + indexBuffer = std::make_shared( + graphicsDevice, + IndexElementSize::SixteenBits, + indices.data(), + indices.size(), + BufferUsage::Immutable); + } + { + // NOTE: Create constant buffer + modelConstantBuffer = std::make_shared( + graphicsDevice, + sizeof(BasicEffect::ModelConstantBuffer), + BufferUsage::Dynamic); + + worldConstantBuffer = std::make_shared( + graphicsDevice, + sizeof(BasicEffect::WorldConstantBuffer), + BufferUsage::Dynamic); + } + { + // NOTE: Create sampler state + sampler = std::make_shared( + graphicsDevice, + SamplerDescription::CreateLinearClamp()); + } + { + auto presentationParameters = graphicsDevice->GetPresentationParameters(); + + BasicEffect::BasicEffectDescription effectDesc; + effectDesc.LightingEnabled = true; + effectDesc.TextureEnabled = true; + effectDesc.VertexColorEnabled = false; + + // NOTE: Create pipeline state + pipelineState1 = BasicEffect::CreateBasicEffect(*assets, effectDesc) + .SetRenderTargetViewFormat(presentationParameters.BackBufferFormat) + .SetDepthStencilViewFormat(presentationParameters.DepthStencilFormat) + .SetDepthStencilState(DepthStencilDescription::CreateDefault()) + .SetBlendState(BlendDescription::CreateNonPremultiplied()) + .SetRasterizerState(RasterizerDescription::CreateDefault()) + .Build(); + } + { + auto presentationParameters = graphicsDevice->GetPresentationParameters(); + + BasicEffect::BasicEffectDescription effectDesc; + effectDesc.LightingEnabled = false; + effectDesc.TextureEnabled = false; + effectDesc.VertexColorEnabled = true; + + // NOTE: Create pipeline state + pipelineState2 = BasicEffect::CreateBasicEffect(*assets, effectDesc) + .SetRenderTargetViewFormat(presentationParameters.BackBufferFormat) + .SetDepthStencilViewFormat(presentationParameters.DepthStencilFormat) + .SetDepthStencilState(DepthStencilDescription::CreateDefault()) + .SetBlendState(BlendDescription::CreateNonPremultiplied()) + .SetRasterizerState(RasterizerDescription::CreateDefault()) + .Build(); + } +} + +void BasicEffectTest::Update() +{ + auto presentationParameters = graphicsDevice->GetPresentationParameters(); + + constexpr float rotateSpeed = 0.5f; + + auto projectionMatrix = Matrix4x4::CreatePerspectiveFieldOfViewLH( + MathHelper::ToRadians(45.0f), + static_cast(presentationParameters.BackBufferWidth) / presentationParameters.BackBufferHeight, + 0.01f, + 1000.0f); + + auto cameraPosition = Vector3{0.0f, 6.0f, 0.0f}; + auto cameraDirection = Vector3{0.0f, -1.0f, 1.0f}; + auto viewMatrix = Matrix4x4::CreateLookAtLH(cameraPosition, cameraPosition + cameraDirection, Vector3::UnitY); + + auto lightDirection = Vector3::Normalize(Vector3{-0.5f, -1.0f, 0.5f}); + + // NOTE: Update constant buffer for world + BasicEffect::WorldConstantBuffer worldConstants; + worldConstants.ViewProjection = viewMatrix * projectionMatrix; + worldConstants.InverseView = Matrix4x4::Invert(viewMatrix); + worldConstants.LightDirection = Vector4{lightDirection, 0.0f}; + worldConstantBuffer->SetValue(worldConstants); + + auto time = static_cast(gameHost->GetClock()->GetTotalGameTime().count()); + auto rotateY = Math::TwoPi * rotateSpeed * time; + + auto mouse = gameHost->GetMouse()->GetState(); + if (mouse.LeftButton == ButtonState::Pressed) { + rotateY = -Math::TwoPi * (static_cast(mouse.Position.X) / static_cast(presentationParameters.BackBufferWidth)); + } + + auto modelMatrix = Matrix4x4::CreateTranslation(Vector3{-0.5f, -0.5f, -0.5f}) + * Matrix4x4::CreateScale(1.0f + std::cos(time * 5.0f) * 0.1f) + * Matrix4x4::CreateRotationY(rotateY) + * Matrix4x4::CreateTranslation(Vector3{0.0f, 0.0f, 6.0f}); + + constexpr float metalness = 0.1f; + + // NOTE: Update constant buffer for model + BasicEffect::ModelConstantBuffer modelConstants; + modelConstants.Model = modelMatrix; + modelConstants.Material = Vector4{metalness, 0.0f, 0.0f, 0.0f}; + modelConstants.Color = Vector4{1.0f, 1.0f, 1.0f, 1.0f}; + modelConstantBuffer->SetValue(modelConstants); +} + +void BasicEffectTest::Draw() +{ + auto presentationParameters = graphicsDevice->GetPresentationParameters(); + + Viewport viewport = {0, 0, presentationParameters.BackBufferWidth, presentationParameters.BackBufferHeight}; + RenderPass pass; + pass.RenderTargets.emplace_back(nullptr, Color::CornflowerBlue.ToVector4()); + pass.ClearDepth = 1.0f; + pass.ClearStencil = 0; + pass.Viewport = viewport; + pass.ScissorRect = viewport.GetBounds(); + + auto mouse = gameHost->GetMouse()->GetState(); + + commandList->Reset(); + commandList->SetRenderPass(std::move(pass)); + commandList->SetConstantBuffer(0, modelConstantBuffer); + commandList->SetConstantBuffer(1, worldConstantBuffer); + commandList->SetSamplerState(0, sampler); + commandList->SetTexture(0, texture); + if (mouse.RightButton == ButtonState::Pressed) { + commandList->SetVertexBuffer(vertexBuffer2); + commandList->SetPipelineState(pipelineState2); + } + else { + commandList->SetVertexBuffer(vertexBuffer1); + commandList->SetPipelineState(pipelineState1); + } + commandList->SetPrimitiveTopology(PrimitiveTopology::TriangleList); + commandList->DrawIndexed(indexBuffer, indexBuffer->GetIndexCount(), 0); + commandList->Close(); + + constexpr bool isStandalone = false; + if constexpr (isStandalone) { + commandQueue->Reset(); + commandQueue->PushbackCommandList(commandList); + commandQueue->ExecuteCommandLists(); + commandQueue->Present(); + } + else { + commandQueue->PushbackCommandList(commandList); + } +} + +} // namespace FeatureShowcase diff --git a/examples/FeatureShowcase/Source/BasicEffectTest/BasicEffectTest.hpp b/examples/FeatureShowcase/Source/BasicEffectTest/BasicEffectTest.hpp new file mode 100644 index 000000000..365a55c7a --- /dev/null +++ b/examples/FeatureShowcase/Source/BasicEffectTest/BasicEffectTest.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace FeatureShowcase { + +using namespace Pomdog; + +class BasicEffectTest final : public Game { +public: + explicit BasicEffectTest(const std::shared_ptr& gameHost); + + void Initialize() override; + + void Update() override; + + void Draw() override; + +private: + std::shared_ptr gameHost; + std::shared_ptr graphicsDevice; + std::shared_ptr commandQueue; + std::shared_ptr commandList; + ConnectionList connect; + + std::shared_ptr vertexBuffer1; + std::shared_ptr vertexBuffer2; + std::shared_ptr indexBuffer; + std::shared_ptr pipelineState1; + std::shared_ptr pipelineState2; + std::shared_ptr sampler; + std::shared_ptr modelConstantBuffer; + std::shared_ptr worldConstantBuffer; + std::shared_ptr texture; +}; + +} // namespace FeatureShowcase diff --git a/examples/FeatureShowcase/Source/FeatureShowcaseGame.cpp b/examples/FeatureShowcase/Source/FeatureShowcaseGame.cpp index 8198e7e9f..7f87e2cee 100644 --- a/examples/FeatureShowcase/Source/FeatureShowcaseGame.cpp +++ b/examples/FeatureShowcase/Source/FeatureShowcaseGame.cpp @@ -1,5 +1,6 @@ #include "FeatureShowcaseGame.hpp" #include "AudioClipTest/AudioClipTest.hpp" +#include "BasicEffectTest/BasicEffectTest.hpp" #include "Beam2DTest/Beam2DTest.hpp" #include "GamepadTest/GamepadTest.hpp" #include "HTTPClientTest/HTTPClientTest.hpp" @@ -58,6 +59,10 @@ void FeatureShowcaseGame::Initialize() window->SetTitle("Feature Showcase > PrimitiveBatch Test"); subGame = std::make_shared(gameHost); }); + buttons.emplace_back("BasicEffect Test", [this] { + window->SetTitle("Feature Showcase > BasicEffect Test"); + subGame = std::make_shared(gameHost); + }); buttons.emplace_back("SpriteBatch Test", [this] { window->SetTitle("Feature Showcase > SpriteBatch Test"); subGame = std::make_shared(gameHost); diff --git a/include/Pomdog/Experimental/Graphics/BasicEffect.hpp b/include/Pomdog/Experimental/Graphics/BasicEffect.hpp new file mode 100644 index 000000000..e1fc3ab93 --- /dev/null +++ b/include/Pomdog/Experimental/Graphics/BasicEffect.hpp @@ -0,0 +1,104 @@ +// Copyright (c) 2013-2019 mogemimi. Distributed under the MIT license. + +#pragma once + +#include "Pomdog/Basic/Export.hpp" +#include "Pomdog/Content/AssetBuilders/Builder.hpp" +#include "Pomdog/Math/Matrix4x4.hpp" +#include "Pomdog/Math/Vector2.hpp" +#include "Pomdog/Math/Vector3.hpp" +#include "Pomdog/Math/Vector4.hpp" + +namespace Pomdog { +class AssetManager; +class PipelineState; +} // namespace Pomdog + +namespace Pomdog::BasicEffect { + +struct VertexPositionColor final { + /// position + Vector3 Position; + + /// vertex color + Vector4 Color; +}; + +struct VertexPositionTexture final { + /// position + Vector3 Position; + + /// texture coordinates + Vector2 TextureCoordinate; +}; + +struct VertexPositionTextureColor final { + /// position + Vector3 Position; + + /// texture coordinates + Vector2 TextureCoordinate; + + /// vertex color + Vector4 Color; +}; + +struct VertexPositionNormalColor final { + /// position + Vector3 Position; + + /// normalized surface normal + Vector3 Normal; + + /// vertex color + Vector4 Color; +}; + +struct VertexPositionNormalTexture final { + /// position + Vector3 Position; + + /// normalized surface normal + Vector3 Normal; + + /// texture coordinates + Vector2 TextureCoordinate; +}; + +struct alignas(16) ModelConstantBuffer final { + Matrix4x4 Model; + + // {x___} = metalness (range: [0.0, 1.0]) + // {_yzw} = unused + Vector4 Material; + + // {xyzw} = color.rgba + Vector4 Color; +}; + +struct alignas(16) WorldConstantBuffer final { + Matrix4x4 ViewProjection; + + Matrix4x4 InverseView; + + // {xyz_} = LightDirection.xyz + // {___w} = unused + Vector4 LightDirection; +}; + +struct POMDOG_EXPORT BasicEffectDescription final { + /// Enables lighting which requires a surface normal in the input layout. + bool LightingEnabled = false; + + /// Enables use of texturing which requires a texture coordinates in the input layout. + bool TextureEnabled = false; + + /// Enables use of a per-vertex color. + bool VertexColorEnabled = false; +}; + +[[nodiscard]] POMDOG_EXPORT +AssetBuilders::Builder +CreateBasicEffect(AssetManager& assets, const BasicEffectDescription& desc); + +} // namespace Pomdog::BasicEffect diff --git a/src/Experimental/Graphics/BasicEffect.cpp b/src/Experimental/Graphics/BasicEffect.cpp new file mode 100644 index 000000000..6acf32fef --- /dev/null +++ b/src/Experimental/Graphics/BasicEffect.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2013-2019 mogemimi. Distributed under the MIT license. + +#include "Pomdog/Experimental/Graphics/BasicEffect.hpp" +#include "Pomdog/Content/AssetBuilders/PipelineStateBuilder.hpp" +#include "Pomdog/Content/AssetBuilders/ShaderBuilder.hpp" +#include "Pomdog/Content/AssetManager.hpp" +#include "Pomdog/Graphics/GraphicsDevice.hpp" +#include "Pomdog/Graphics/InputLayoutHelper.hpp" +#include "Pomdog/Graphics/PipelineState.hpp" +#include "Pomdog/Graphics/PresentationParameters.hpp" +#include "Pomdog/Graphics/Shader.hpp" +#include "Pomdog/Graphics/ShaderPipelineStage.hpp" + +namespace Pomdog::BasicEffect { +namespace { + +// Built-in shaders +#include "Shaders/GLSL.Embedded/BasicEffect_PS.inc.hpp" +#include "Shaders/GLSL.Embedded/BasicEffect_VS.inc.hpp" +#include "Shaders/HLSL.Embedded/BasicEffect.inc.hpp" +#include "Shaders/Metal.Embedded/BasicEffect.inc.hpp" + +} // namespace + +AssetBuilders::Builder +CreateBasicEffect(AssetManager& assets, const BasicEffectDescription& desc) +{ + InputLayoutHelper inputLayoutBuilder; + inputLayoutBuilder.Float3(); + + if (desc.LightingEnabled) { + // NOTE: Add surface normal into the input layout. + inputLayoutBuilder.Float3(); + } + + if (desc.TextureEnabled) { + // NOTE: Add texture coordinates into the input layout. + inputLayoutBuilder.Float2(); + } + else { + // NOTE: Add vertex color into the input layout. + inputLayoutBuilder.Float4(); + } + + // NOTE: Generate shader code for Metal + std::string preprocessorMacroLines; + if (desc.LightingEnabled) { + preprocessorMacroLines += "#define LIGHTING_ENABLED 1\n"; + } + else { + preprocessorMacroLines += "#define LIGHTING_ENABLED 0\n"; + } + if (desc.TextureEnabled) { + preprocessorMacroLines += "#define TEXTURE_ENABLED 1\n"; + preprocessorMacroLines += "#define VERTEX_COLOR_ENABLED 0\n"; + } + else { + preprocessorMacroLines += "#define TEXTURE_ENABLED 0\n"; + preprocessorMacroLines += "#define VERTEX_COLOR_ENABLED 1\n"; + } + + std::string sourceHLSL = preprocessorMacroLines + BuiltinHLSL_BasicEffect; + std::string sourceMetal = preprocessorMacroLines + Builtin_Metal_BasicEffect; + std::string sourceGLSLVS = "#version 330\n" + preprocessorMacroLines + Builtin_GLSL_BasicEffect_VS; + std::string sourceGLSLPS = "#version 330\n" + preprocessorMacroLines + Builtin_GLSL_BasicEffect_PS; + + auto inputLayout = inputLayoutBuilder.CreateInputLayout(); + + auto vertexShaderBuilder = assets.CreateBuilder(ShaderPipelineStage::VertexShader); + + auto vertexShader = vertexShaderBuilder + .SetGLSL(sourceGLSLVS.data(), sourceGLSLVS.size()) + .SetHLSL(sourceHLSL.data(), sourceHLSL.size(), "BasicEffectVS") + .SetMetal(sourceMetal.data(), sourceMetal.size(), "BasicEffectVS") + .Build(); + + auto pixelShaderBuilder = assets.CreateBuilder(ShaderPipelineStage::PixelShader); + + auto pixelShader = pixelShaderBuilder + .SetGLSL(sourceGLSLPS.data(), sourceGLSLPS.size()) + .SetHLSL(sourceHLSL.data(), sourceHLSL.size(), "BasicEffectPS") + .SetMetal(sourceMetal.data(), sourceMetal.size(), "BasicEffectPS") + .Build(); + + auto graphicsDevice = assets.GetGraphicsDevice(); + + auto builder = assets.CreateBuilder(); + builder.SetInputLayout(inputLayout); + builder.SetVertexShader(std::move(vertexShader)); + builder.SetPixelShader(std::move(pixelShader)); + builder.SetConstantBufferBindSlot("ModelConstantBuffer", 0); + builder.SetConstantBufferBindSlot("WorldConstantBuffer", 1); + + return builder; +} + +} // namespace Pomdog::BasicEffect diff --git a/src/Experimental/Graphics/Shaders/GLSL.Embedded/BasicEffect_PS.inc.hpp b/src/Experimental/Graphics/Shaders/GLSL.Embedded/BasicEffect_PS.inc.hpp new file mode 100644 index 000000000..c8d87a919 --- /dev/null +++ b/src/Experimental/Graphics/Shaders/GLSL.Embedded/BasicEffect_PS.inc.hpp @@ -0,0 +1,76 @@ +// Copyright (c) 2013-2019 mogemimi. Distributed under the MIT license. + +constexpr auto Builtin_GLSL_BasicEffect_PS = R"( + +uniform ModelConstantBuffer{ +mat4x4 Model; +vec4 Material; +vec4 DiffuseColor;}; +uniform WorldConstantBuffer{ +mat4x4 ViewProjection; +mat4x4 InverseView; +vec4 LightDirection;}; +in VertexData{ +#if LIGHTING_ENABLED +vec3 WorldSpacePosition; +vec3 Normal; +#endif +#if TEXTURE_ENABLED +vec2 TextureCoord; +#endif +#if VERTEX_COLOR_ENABLED +vec4 Color; +#endif +}In; +uniform sampler2D DiffuseTexture; +out vec4 FragColor; +#if LIGHTING_ENABLED +float LambertianDiffuse(vec3 lightDirection,vec3 surfaceNormal){ +return max(dot(lightDirection,surfaceNormal),0.0);} +float ComputePhongSpecular( +vec3 viewDirection, +vec3 lightDirection, +vec3 worldSpaceNormal, +float specular){ +vec3 halfVector=normalize(lightDirection.xyz+viewDirection); +float specularAngle=max(0.0,dot(halfVector,worldSpaceNormal)); +float density=50.0; +return pow(specularAngle,density)*specular;} +float ComputeLight( +vec3 viewDirection, +vec3 inverseLightDirection, +vec3 surfaceNormal, +float specular){ +float kd=LambertianDiffuse(inverseLightDirection,surfaceNormal); +kd=kd*0.5+0.5; +kd=kd*kd; +float ks=ComputePhongSpecular( +viewDirection, +inverseLightDirection, +surfaceNormal, +1.0); +return kd+ks;} +#endif +void main(){ +#if TEXTURE_ENABLED +vec4 albedoColor=texture(DiffuseTexture,In.TextureCoord.xy); +#elif VERTEX_COLOR_ENABLED +vec4 albedoColor=In.Color.rgba; +#else +vec4 albedoColor=vec4(1.0,1.0,1.0,1.0); +#endif +#if LIGHTING_ENABLED +vec3 worldSpaceCameraPosition=(InverseView*vec4(0.0,0.0,0.0,1.0)).xyz; +vec3 viewDirection=normalize(worldSpaceCameraPosition-In.WorldSpacePosition); +float metalness=Material.x; +float directionalLightReflectance=ComputeLight( +viewDirection, +-LightDirection.xyz, +normalize(In.Normal), +metalness); +FragColor=vec4(albedoColor.rgb*directionalLightReflectance,albedoColor.a); +#else +FragColor=albedoColor; +#endif +} +)"; diff --git a/src/Experimental/Graphics/Shaders/GLSL.Embedded/BasicEffect_VS.inc.hpp b/src/Experimental/Graphics/Shaders/GLSL.Embedded/BasicEffect_VS.inc.hpp new file mode 100644 index 000000000..53aa27771 --- /dev/null +++ b/src/Experimental/Graphics/Shaders/GLSL.Embedded/BasicEffect_VS.inc.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2013-2019 mogemimi. Distributed under the MIT license. + +constexpr auto Builtin_GLSL_BasicEffect_VS = R"( + +layout(location=0)in vec3 Position; +#if LIGHTING_ENABLED +layout(location=1)in vec3 Normal; +#if TEXTURE_ENABLED +layout(location=2)in vec2 TextureCoord; +#else +layout(location=2)in vec4 Color; +#endif +#else +#if TEXTURE_ENABLED +layout(location=1)in vec2 TextureCoord; +#else +layout(location=1)in vec4 Color; +#endif +#endif +uniform ModelConstantBuffer{ +mat4x4 Model; +vec4 Material; +vec4 DiffuseColor;}; +uniform WorldConstantBuffer{ +mat4x4 ViewProjection; +mat4x4 InverseView; +vec4 LightDirection;}; +out VertexData{ +#if LIGHTING_ENABLED +vec3 WorldSpacePosition; +vec3 Normal; +#endif +#if TEXTURE_ENABLED +vec2 TextureCoord; +#endif +#if VERTEX_COLOR_ENABLED +vec4 Color; +#endif +}Out; +void main(){ +vec4 worldSpacePosition=Model*vec4(Position.xyz,1.0); +gl_Position=ViewProjection*worldSpacePosition; +#if LIGHTING_ENABLED +Out.WorldSpacePosition=worldSpacePosition.xyz; +Out.Normal=mat3x3(Model)*Normal.xyz; +#endif +#if TEXTURE_ENABLED +Out.TextureCoord=TextureCoord.xy; +#endif +#if VERTEX_COLOR_ENABLED +Out.Color=Color.rgba; +#endif +} +)"; diff --git a/src/Experimental/Graphics/Shaders/GLSL/BasicEffect_PS.glsl b/src/Experimental/Graphics/Shaders/GLSL/BasicEffect_PS.glsl new file mode 100644 index 000000000..f18bad01f --- /dev/null +++ b/src/Experimental/Graphics/Shaders/GLSL/BasicEffect_PS.glsl @@ -0,0 +1,106 @@ +// #version 330 + +uniform ModelConstantBuffer { + mat4x4 Model; + + // {x___} = metalness (range: [0.0, 1.0]) + // {_yzw} = unused + vec4 Material; + + // {xyzw} = color.rgba + vec4 DiffuseColor; +}; + +uniform WorldConstantBuffer { + mat4x4 ViewProjection; + mat4x4 InverseView; + + // {xyz_} = LightDirection.xyz + // {___w} = unused + vec4 LightDirection; +}; + +in VertexData { +#if LIGHTING_ENABLED + vec3 WorldSpacePosition; + vec3 Normal; +#endif +#if TEXTURE_ENABLED + vec2 TextureCoord; +#endif +#if VERTEX_COLOR_ENABLED + vec4 Color; +#endif +} In; + +uniform sampler2D DiffuseTexture; + +out vec4 FragColor; + +#if LIGHTING_ENABLED +float LambertianDiffuse(vec3 lightDirection, vec3 surfaceNormal) +{ + // NOTE: 'lightDirection' and 'surfaceNormal' have already been normalized. + return max(dot(lightDirection, surfaceNormal), 0.0); +} + +float ComputePhongSpecular( + vec3 viewDirection, + vec3 lightDirection, + vec3 worldSpaceNormal, + float specular) +{ + vec3 halfVector = normalize(lightDirection.xyz + viewDirection); + float specularAngle = max(0.0, dot(halfVector, worldSpaceNormal)); + float density = 50.0; + return pow(specularAngle, density) * specular; +} + +float ComputeLight( + vec3 viewDirection, + vec3 inverseLightDirection, + vec3 surfaceNormal, + float specular) +{ + float kd = LambertianDiffuse(inverseLightDirection, surfaceNormal); + + // NOTE: Half-lambert + kd = kd * 0.5 + 0.5; + kd = kd * kd; + + float ks = ComputePhongSpecular( + viewDirection, + inverseLightDirection, + surfaceNormal, + 1.0); + + return kd + ks; +} +#endif + +void main() +{ +#if TEXTURE_ENABLED + vec4 albedoColor = texture(DiffuseTexture, In.TextureCoord.xy); +#elif VERTEX_COLOR_ENABLED + vec4 albedoColor = In.Color.rgba; +#else + vec4 albedoColor = vec4(1.0, 1.0, 1.0, 1.0); +#endif + +#if LIGHTING_ENABLED + vec3 worldSpaceCameraPosition = (InverseView * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + vec3 viewDirection = normalize(worldSpaceCameraPosition - In.WorldSpacePosition); + float metalness = Material.x; + + float directionalLightReflectance = ComputeLight( + viewDirection, + -LightDirection.xyz, + normalize(In.Normal), + metalness); + + FragColor = vec4(albedoColor.rgb * directionalLightReflectance, albedoColor.a); +#else + FragColor = albedoColor; +#endif +} diff --git a/src/Experimental/Graphics/Shaders/GLSL/BasicEffect_VS.glsl b/src/Experimental/Graphics/Shaders/GLSL/BasicEffect_VS.glsl new file mode 100644 index 000000000..d667c5610 --- /dev/null +++ b/src/Experimental/Graphics/Shaders/GLSL/BasicEffect_VS.glsl @@ -0,0 +1,68 @@ +// #version 330 + +layout(location = 0) in vec3 Position; + +#if LIGHTING_ENABLED +layout(location = 1) in vec3 Normal; +#if TEXTURE_ENABLED +layout(location = 2) in vec2 TextureCoord; +#else +layout(location = 2) in vec4 Color; +#endif +#else +#if TEXTURE_ENABLED +layout(location = 1) in vec2 TextureCoord; +#else +layout(location = 1) in vec4 Color; +#endif +#endif + +uniform ModelConstantBuffer { + mat4x4 Model; + + // {x___} = metalness (range: [0.0, 1.0]) + // {_yzw} = unused + vec4 Material; + + // {xyzw} = color.rgba + vec4 DiffuseColor; +}; + +uniform WorldConstantBuffer { + mat4x4 ViewProjection; + mat4x4 InverseView; + + // {xyz_} = LightDirection.xyz + // {___w} = unused + vec4 LightDirection; +}; + +out VertexData { +#if LIGHTING_ENABLED + vec3 WorldSpacePosition; + vec3 Normal; +#endif +#if TEXTURE_ENABLED + vec2 TextureCoord; +#endif +#if VERTEX_COLOR_ENABLED + vec4 Color; +#endif +} Out; + +void main() +{ + vec4 worldSpacePosition = Model * vec4(Position.xyz, 1.0); + gl_Position = ViewProjection * worldSpacePosition; + +#if LIGHTING_ENABLED + Out.WorldSpacePosition = worldSpacePosition.xyz; + Out.Normal = mat3x3(Model) * Normal.xyz; +#endif +#if TEXTURE_ENABLED + Out.TextureCoord = TextureCoord.xy; +#endif +#if VERTEX_COLOR_ENABLED + Out.Color = Color.rgba; +#endif +} diff --git a/src/Experimental/Graphics/Shaders/HLSL.Embedded/BasicEffect.inc.hpp b/src/Experimental/Graphics/Shaders/HLSL.Embedded/BasicEffect.inc.hpp new file mode 100644 index 000000000..2bd710819 --- /dev/null +++ b/src/Experimental/Graphics/Shaders/HLSL.Embedded/BasicEffect.inc.hpp @@ -0,0 +1,111 @@ +// Copyright (c) 2013-2019 mogemimi. Distributed under the MIT license. + +constexpr auto BuiltinHLSL_BasicEffect = R"( +cbuffer ModelConstantBuffer : register(b0){ +matrixModel; +float4 Material; +float4 DiffuseColor; }; +cbuffer WorldConstantBuffer : register(b1){ +matrixViewProjection; +matrixInverseView; +float4 LightDirection; }; +struct VertexShaderInput{ +float3 Position : SV_Position; +#if LIGHTING_ENABLED +float3 Normal : NORMAL0; +#endif +#if TEXTURE_ENABLED +float2 TextureCoord : TEXCOORD0; +#endif +#if VERTEX_COLOR_ENABLED +float4 Color : COLOR0; +#endif +}; +struct VertexShaderOutput{ +float4 Position : SV_Position; +#if LIGHTING_ENABLED +float3 WorldSpacePosition : COLOR1; +float3 Normal : NORMAL0; +#endif +#if TEXTURE_ENABLED +float2 TextureCoord : TEXCOORD0; +#endif +#if VERTEX_COLOR_ENABLED +float4 Color : COLOR0; +#endif +}; +#if LIGHTING_ENABLED +float LambertianDiffuse(float3 lightDirection,float3 surfaceNormal) { +return max(dot(lightDirection,surfaceNormal),0.0f); } +float ComputePhongSpecular( +float3 viewDirection, +float3 lightDirection, +float3 worldSpaceNormal, +float specular) { +float3 halfVector=normalize(lightDirection.xyz+viewDirection); +float specularAngle=max(0.0f,dot(halfVector,worldSpaceNormal)); +float density=50.0f; +return pow(specularAngle,density)*specular; } +float ComputeLight( +float3 viewDirection, +float3 inverseLightDirection, +float3 surfaceNormal, +float specular) { +float kd=LambertianDiffuse(inverseLightDirection,surfaceNormal); +kd=kd*0.5f+0.5f; +kd=kd*kd; +float ks=ComputePhongSpecular( +viewDirection, +inverseLightDirection, +surfaceNormal, +1.0); +return kd+ks; } +#endif +VertexShaderOutput BasicEffectVS(VertexShaderInput input) { +VertexShaderOutput output=(VertexShaderOutput)0; +float4 worldSpacePosition=mul(Model,float4(input.Position,1.0f)); +#if LIGHTING_ENABLED +float3x3 model3x3; +model3x3[0].xyz=Model[0].xyz; +model3x3[1].xyz=Model[1].xyz; +model3x3[2].xyz=Model[2].xyz; +#endif +output.Position=mul(ViewProjection,worldSpacePosition); +#if LIGHTING_ENABLED +output.WorldSpacePosition=worldSpacePosition.xyz; +output.Normal=mul(model3x3,input.Normal.xyz); +#endif +#if TEXTURE_ENABLED +output.TextureCoord=input.TextureCoord.xy; +#endif +#if VERTEX_COLOR_ENABLED +output.Color=input.Color.rgba; +#endif +return output; } +#if TEXTURE_ENABLED +Texture2DdiffuseTexture : register(t0); +SamplerState textureSampler : register(s0); +#endif +float4 BasicEffectPS(VertexShaderOutput input): SV_Target { +#if TEXTURE_ENABLED +float4 albedoColor=diffuseTexture.Sample(textureSampler,input.TextureCoord.xy); +#elif VERTEX_COLOR_ENABLED +float4 albedoColor=input.Color.rgba; +#else +float4 albedoColor=float4(1.0f,1.0f,1.0f,1.0f); +#endif +#if LIGHTING_ENABLED +float3 worldSpaceCameraPosition=(mul(InverseView,float4(0.0f,0.0f,0.0f,1.0f))).xyz; +float3 viewDirection=normalize(worldSpaceCameraPosition-input.WorldSpacePosition); +float metalness=Material.x; +float directionalLightReflectance=ComputeLight( +viewDirection, +-LightDirection.xyz, +normalize(input.Normal), +metalness); +return float4(albedoColor.rgb*directionalLightReflectance,albedoColor.a); +#else +return albedoColor; +#endif +} +)"; diff --git a/src/Experimental/Graphics/Shaders/HLSL/BasicEffect.hlsl b/src/Experimental/Graphics/Shaders/HLSL/BasicEffect.hlsl new file mode 100644 index 000000000..77a8fad72 --- /dev/null +++ b/src/Experimental/Graphics/Shaders/HLSL/BasicEffect.hlsl @@ -0,0 +1,149 @@ +cbuffer ModelConstantBuffer : register(b0) { + matrix Model; + + // {x___} = metalness (range: [0.0, 1.0]) + // {_yzw} = unused + float4 Material; + + // {xyzw} = color.rgba + float4 DiffuseColor; +}; + +cbuffer WorldConstantBuffer : register(b1) { + matrix ViewProjection; + matrix InverseView; + + // {xyz_} = LightDirection.xyz + // {___w} = unused + float4 LightDirection; +}; + +struct VertexShaderInput { + // {xyz_} = position.xyz + float3 Position : SV_Position; + +#if LIGHTING_ENABLED + float3 Normal : NORMAL0; +#endif +#if TEXTURE_ENABLED + float2 TextureCoord : TEXCOORD0; +#endif +#if VERTEX_COLOR_ENABLED + float4 Color : COLOR0; +#endif +}; + +struct VertexShaderOutput { + float4 Position : SV_Position; +#if LIGHTING_ENABLED + float3 WorldSpacePosition : COLOR1; + float3 Normal : NORMAL0; +#endif +#if TEXTURE_ENABLED + float2 TextureCoord : TEXCOORD0; +#endif +#if VERTEX_COLOR_ENABLED + float4 Color : COLOR0; +#endif +}; + +#if LIGHTING_ENABLED +float LambertianDiffuse(float3 lightDirection, float3 surfaceNormal) +{ + // NOTE: 'lightDirection' and 'surfaceNormal' have already been normalized. + return max(dot(lightDirection, surfaceNormal), 0.0f); +} + +float ComputePhongSpecular( + float3 viewDirection, + float3 lightDirection, + float3 worldSpaceNormal, + float specular) +{ + float3 halfVector = normalize(lightDirection.xyz + viewDirection); + float specularAngle = max(0.0f, dot(halfVector, worldSpaceNormal)); + float density = 50.0f; + return pow(specularAngle, density) * specular; +} + +float ComputeLight( + float3 viewDirection, + float3 inverseLightDirection, + float3 surfaceNormal, + float specular) +{ + float kd = LambertianDiffuse(inverseLightDirection, surfaceNormal); + + // NOTE: Half-lambert + kd = kd * 0.5f + 0.5f; + kd = kd * kd; + + float ks = ComputePhongSpecular( + viewDirection, + inverseLightDirection, + surfaceNormal, + 1.0); + + return kd + ks; +} +#endif + +VertexShaderOutput BasicEffectVS(VertexShaderInput input) +{ + VertexShaderOutput output = (VertexShaderOutput)0; + + float4 worldSpacePosition = mul(Model, float4(input.Position, 1.0f)); + +#if LIGHTING_ENABLED + // NOTE: Convert float4x4 to float3x3 + float3x3 model3x3; + model3x3[0].xyz = Model[0].xyz; + model3x3[1].xyz = Model[1].xyz; + model3x3[2].xyz = Model[2].xyz; +#endif + + output.Position = mul(ViewProjection, worldSpacePosition); +#if LIGHTING_ENABLED + output.WorldSpacePosition = worldSpacePosition.xyz; + output.Normal = mul(model3x3, input.Normal.xyz); +#endif +#if TEXTURE_ENABLED + output.TextureCoord = input.TextureCoord.xy; +#endif +#if VERTEX_COLOR_ENABLED + output.Color = input.Color.rgba; +#endif + return output; +} + +#if TEXTURE_ENABLED +Texture2D diffuseTexture : register(t0); +SamplerState textureSampler : register(s0); +#endif + +float4 BasicEffectPS(VertexShaderOutput input) : SV_Target +{ +#if TEXTURE_ENABLED + float4 albedoColor = diffuseTexture.Sample(textureSampler, input.TextureCoord.xy); +#elif VERTEX_COLOR_ENABLED + float4 albedoColor = input.Color.rgba; +#else + float4 albedoColor = float4(1.0f, 1.0f, 1.0f, 1.0f); +#endif + +#if LIGHTING_ENABLED + float3 worldSpaceCameraPosition = (mul(InverseView, float4(0.0f, 0.0f, 0.0f, 1.0f))).xyz; + float3 viewDirection = normalize(worldSpaceCameraPosition - input.WorldSpacePosition); + float metalness = Material.x; + + float directionalLightReflectance = ComputeLight( + viewDirection, + -LightDirection.xyz, + normalize(input.Normal), + metalness); + + return float4(albedoColor.rgb * directionalLightReflectance, albedoColor.a); +#else + return albedoColor; +#endif +} diff --git a/src/Experimental/Graphics/Shaders/Metal.Embedded/BasicEffect.inc.hpp b/src/Experimental/Graphics/Shaders/Metal.Embedded/BasicEffect.inc.hpp new file mode 100644 index 000000000..e6225fb2e --- /dev/null +++ b/src/Experimental/Graphics/Shaders/Metal.Embedded/BasicEffect.inc.hpp @@ -0,0 +1,137 @@ +// Copyright (c) 2013-2019 mogemimi. Distributed under the MIT license. + +constexpr auto Builtin_Metal_BasicEffect = R"( +#include +#include +using namespace metal; +struct __attribute__((__aligned__(256)))ModelConstantBuffer{ +matrix_float4x4 Model; +float4 Material; +float4 Color;}; +struct __attribute__((__aligned__(256)))WorldConstantBuffer{ +matrix_float4x4 ViewProjection; +matrix_float4x4 InverseView; +float4 LightDirection;}; +struct VertexPositionColor{ +float3 Position [[attribute(0)]]; +float4 Color [[attribute(1)]];}; +struct VertexPositionTexture{ +float3 Position [[attribute(0)]]; +float2 TextureCoord [[attribute(1)]];}; +struct VertexPositionNormalColor{ +float3 Position [[attribute(0)]]; +float3 Normal [[attribute(1)]]; +float4 Color [[attribute(2)]];}; +struct VertexPositionNormalTexture{ +float3 Position [[attribute(0)]]; +float3 Normal [[attribute(1)]]; +float2 TextureCoord [[attribute(2)]];}; +struct VertexShaderOutput{ +float4 Position [[position]]; +#if LIGHTING_ENABLED +float3 WorldSpacePosition; +float3 Normal; +#endif +#if TEXTURE_ENABLED +float2 TextureCoord; +#endif +#if VERTEX_COLOR_ENABLED +float4 Color; +#endif +}; +namespace{ +#if LIGHTING_ENABLED +float LambertianDiffuse(float3 lightDirection,float3 surfaceNormal){ +return max(dot(lightDirection,surfaceNormal),0.0);} +float ComputePhongSpecular( +float3 viewDirection, +float3 lightDirection, +float3 worldSpaceNormal, +float specular){ +float3 halfVector=normalize(lightDirection.xyz+viewDirection); +float specularAngle=max(0.0,dot(halfVector,worldSpaceNormal)); +const float density=50.0; +return pow(specularAngle,density)*specular;} +float ComputeLight( +float3 viewDirection, +float3 inverseLightDirection, +float3 surfaceNormal, +float specular){ +float kd=LambertianDiffuse(inverseLightDirection,surfaceNormal); +kd=kd*0.5+0.5; +kd=kd*kd; +float ks=ComputePhongSpecular( +viewDirection, +inverseLightDirection, +surfaceNormal, +1.0); +return kd+ks;} +#endif +} +vertex VertexShaderOutput BasicEffectVS( +#if LIGHTING_ENABLED +#if TEXTURE_ENABLED +VertexPositionNormalTexture in [[stage_in]], +#else +VertexPositionNormalColor in [[stage_in]], +#endif +#else +#if TEXTURE_ENABLED +VertexPositionTexture in [[stage_in]], +#else +VertexPositionColor in [[stage_in]], +#endif +#endif +constant ModelConstantBuffer& modelConstants [[buffer(0)]], +constant WorldConstantBuffer& worldConstants [[buffer(1)]]){ +VertexShaderOutput out; +float4 worldSpacePosition=modelConstants.Model*float4(in.Position,1.0); +#if LIGHTING_ENABLED +float3x3 model3x3; +model3x3[0].xyz=modelConstants.Model[0].xyz; +model3x3[1].xyz=modelConstants.Model[1].xyz; +model3x3[2].xyz=modelConstants.Model[2].xyz; +#endif +out.Position=worldConstants.ViewProjection*worldSpacePosition; +#if LIGHTING_ENABLED +out.WorldSpacePosition=worldSpacePosition.xyz; +out.Normal=model3x3*in.Normal.xyz; +#endif +#if TEXTURE_ENABLED +out.TextureCoord=in.TextureCoord.xy; +#endif +#if VERTEX_COLOR_ENABLED +out.Color=in.Color.rgba; +#endif +return out;} +fragment half4 BasicEffectPS( +VertexShaderOutput in [[stage_in]], +#if TEXTURE_ENABLED +texture2ddiffuseTexture [[texture(0)]], +sampler textureSampler [[sampler(0)]], +#endif +constant ModelConstantBuffer& modelConstants [[buffer(0)]], +constant WorldConstantBuffer& worldConstants [[buffer(1)]]){ +#if TEXTURE_ENABLED +float4 albedoColor=diffuseTexture.sample(textureSampler,in.TextureCoord); +#elif VERTEX_COLOR_ENABLED +float4 albedoColor=in.Color.rgba; +#else +float4 albedoColor=float4(1.0,1.0,1.0,1.0); +#endif +#if LIGHTING_ENABLED +float3 worldSpaceCameraPosition=(worldConstants.InverseView*float4(0.0,0.0,0.0,1.0)).xyz; +float3 viewDirection=normalize(worldSpaceCameraPosition-in.WorldSpacePosition); +float metalness=modelConstants.Material.x; +float directionalLightReflectance=ComputeLight( +viewDirection, +-worldConstants.LightDirection.xyz, +normalize(in.Normal), +metalness); +float4 color=float4(albedoColor.rgb*directionalLightReflectance,albedoColor.a); +return half4(color); +#else +return half4(albedoColor); +#endif +} +)"; diff --git a/src/Experimental/Graphics/Shaders/Metal/BasicEffect.metal b/src/Experimental/Graphics/Shaders/Metal/BasicEffect.metal new file mode 100644 index 000000000..36e913012 --- /dev/null +++ b/src/Experimental/Graphics/Shaders/Metal/BasicEffect.metal @@ -0,0 +1,183 @@ +#include +#include + +using namespace metal; + +struct __attribute__((__aligned__(256))) ModelConstantBuffer { + matrix_float4x4 Model; + + // {x___} = metalness (range: [0.0, 1.0]) + // {_yzw} = unused + float4 Material; + + // {xyzw} = color.rgba + float4 Color; +}; + +struct __attribute__((__aligned__(256))) WorldConstantBuffer { + matrix_float4x4 ViewProjection; + matrix_float4x4 InverseView; + + // {xyz_} = LightDirection.xyz + // {___w} = unused + float4 LightDirection; +}; + +struct VertexPositionColor { + float3 Position [[attribute(0)]]; + float4 Color [[attribute(1)]]; +}; + +struct VertexPositionTexture { + float3 Position [[attribute(0)]]; + float2 TextureCoord [[attribute(1)]]; +}; + +struct VertexPositionNormalColor { + float3 Position [[attribute(0)]]; + float3 Normal [[attribute(1)]]; + float4 Color [[attribute(2)]]; +}; + +struct VertexPositionNormalTexture { + float3 Position [[attribute(0)]]; + float3 Normal [[attribute(1)]]; + float2 TextureCoord [[attribute(2)]]; +}; + +struct VertexShaderOutput { + float4 Position [[position]]; +#if LIGHTING_ENABLED + float3 WorldSpacePosition; + float3 Normal; +#endif +#if TEXTURE_ENABLED + float2 TextureCoord; +#endif +#if VERTEX_COLOR_ENABLED + float4 Color; +#endif +}; + +namespace { + +#if LIGHTING_ENABLED +float LambertianDiffuse(float3 lightDirection, float3 surfaceNormal) +{ + // NOTE: 'lightDirection' and 'surfaceNormal' have already been normalized. + return max(dot(lightDirection, surfaceNormal), 0.0); +} + +float ComputePhongSpecular( + float3 viewDirection, + float3 lightDirection, + float3 worldSpaceNormal, + float specular) +{ + float3 halfVector = normalize(lightDirection.xyz + viewDirection); + float specularAngle = max(0.0, dot(halfVector, worldSpaceNormal)); + const float density = 50.0; + return pow(specularAngle, density) * specular; +} + +float ComputeLight( + float3 viewDirection, + float3 inverseLightDirection, + float3 surfaceNormal, + float specular) +{ + float kd = LambertianDiffuse(inverseLightDirection, surfaceNormal); + + // NOTE: Half-lambert + kd = kd * 0.5 + 0.5; + kd = kd * kd; + + float ks = ComputePhongSpecular( + viewDirection, + inverseLightDirection, + surfaceNormal, + 1.0); + + return kd + ks; +} +#endif + +} // namespace + +vertex VertexShaderOutput BasicEffectVS( +#if LIGHTING_ENABLED +#if TEXTURE_ENABLED + VertexPositionNormalTexture in [[stage_in]], +#else + VertexPositionNormalColor in [[stage_in]], +#endif +#else +#if TEXTURE_ENABLED + VertexPositionTexture in [[stage_in]], +#else + VertexPositionColor in [[stage_in]], +#endif +#endif + constant ModelConstantBuffer& modelConstants [[buffer(0)]], + constant WorldConstantBuffer& worldConstants [[buffer(1)]]) +{ + VertexShaderOutput out; + + float4 worldSpacePosition = modelConstants.Model * float4(in.Position, 1.0); + +#if LIGHTING_ENABLED + // NOTE: Convert float4x4 to float3x3 + float3x3 model3x3; + model3x3[0].xyz = modelConstants.Model[0].xyz; + model3x3[1].xyz = modelConstants.Model[1].xyz; + model3x3[2].xyz = modelConstants.Model[2].xyz; +#endif + + out.Position = worldConstants.ViewProjection * worldSpacePosition; +#if LIGHTING_ENABLED + out.WorldSpacePosition = worldSpacePosition.xyz; + out.Normal = model3x3 * in.Normal.xyz; +#endif +#if TEXTURE_ENABLED + out.TextureCoord = in.TextureCoord.xy; +#endif +#if VERTEX_COLOR_ENABLED + out.Color = in.Color.rgba; +#endif + return out; +} + +fragment half4 BasicEffectPS( + VertexShaderOutput in [[stage_in]], +#if TEXTURE_ENABLED + texture2d diffuseTexture [[texture(0)]], + sampler textureSampler [[sampler(0)]], +#endif + constant ModelConstantBuffer& modelConstants [[buffer(0)]], + constant WorldConstantBuffer& worldConstants [[buffer(1)]]) +{ +#if TEXTURE_ENABLED + float4 albedoColor = diffuseTexture.sample(textureSampler, in.TextureCoord); +#elif VERTEX_COLOR_ENABLED + float4 albedoColor = in.Color.rgba; +#else + float4 albedoColor = float4(1.0, 1.0, 1.0, 1.0); +#endif + +#if LIGHTING_ENABLED + float3 worldSpaceCameraPosition = (worldConstants.InverseView * float4(0.0, 0.0, 0.0, 1.0)).xyz; + float3 viewDirection = normalize(worldSpaceCameraPosition - in.WorldSpacePosition); + float metalness = modelConstants.Material.x; + + float directionalLightReflectance = ComputeLight( + viewDirection, + -worldConstants.LightDirection.xyz, + normalize(in.Normal), + metalness); + + float4 color = float4(albedoColor.rgb * directionalLightReflectance, albedoColor.a); + return half4(color); +#else + return half4(albedoColor); +#endif +}