add vertexarray, move Sandbox::Demolayer code to EditorLayer

This commit is contained in:
2025-11-29 11:35:06 +08:00
parent 4c7b79ec8a
commit abdfdd1858
39 changed files with 63 additions and 773 deletions

View File

@ -1,658 +0,0 @@
//
// Created by sfd on 25-11-21.
//
#include "DemoLayer.h"
#include "Prism/Renderer/Renderer.h"
#include "Prism/Renderer/Shader.h"
namespace {
enum class PropertyFlag
{
None = 0, ColorProperty = 1
};
void Property(const std::string& name, bool& value)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
std::string id = "##" + name;
ImGui::Checkbox(id.c_str(), &value);
ImGui::PopItemWidth();
ImGui::NextColumn();
}
void Property(const std::string& name, float& value, float min = -1.0f, float max = 1.0f, PropertyFlag flags = PropertyFlag::None)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
std::string id = "##" + name;
ImGui::SliderFloat(id.c_str(), &value, min, max);
ImGui::PopItemWidth();
ImGui::NextColumn();
}
void Property(const std::string& name, glm::vec3& value, float min = -1.0f, float max = 1.0f, PropertyFlag flags = PropertyFlag::None)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
std::string id = "##" + name;
if ((int)flags & (int)PropertyFlag::ColorProperty)
ImGui::ColorEdit3(id.c_str(), glm::value_ptr(value), ImGuiColorEditFlags_NoInputs);
else
ImGui::SliderFloat3(id.c_str(), glm::value_ptr(value), min, max);
ImGui::PopItemWidth();
ImGui::NextColumn();
}
void Property(const std::string& name, glm::vec3& value, PropertyFlag flags)
{
Property(name, value, -1.0f, 1.0f, flags);
}
void Property(const std::string& name, glm::vec4& value, float min = -1.0f, float max = 1.0f, PropertyFlag flags = PropertyFlag::None)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
std::string id = "##" + name;
if ((int)flags & (int)PropertyFlag::ColorProperty)
ImGui::ColorEdit4(id.c_str(), glm::value_ptr(value), ImGuiColorEditFlags_NoInputs);
else
ImGui::SliderFloat4(id.c_str(), glm::value_ptr(value), min, max);
ImGui::PopItemWidth();
ImGui::NextColumn();
}
void Property(const std::string& name, glm::vec4& value, PropertyFlag flags)
{
Property(name, value, -1.0f, 1.0f, flags);
}
static void ImGuiShowHelpMarker(const char* desc)
{
ImGui::TextDisabled("(?)");
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(desc);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
}
DemoLayer::DemoLayer()
: m_ClearColor{ 0.1f, 0.1f, 0.1f, 1.0f }, m_Scene(Scene::Model),
m_Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f))
{
}
DemoLayer::~DemoLayer()
{
}
void DemoLayer::OnAttach()
{
m_Framebuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA16F));
m_FinalPresentBuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA8));
m_QuadShader = Prism::Shader::Create("assets/shaders/quad.glsl");
m_HDRShader= Prism::Shader::Create("assets/shaders/hdr.glsl");
m_GridShader = Prism::Shader::Create("assets/shaders/Grid.glsl");
m_Mesh = Prism::CreateRef<Prism::Mesh>("assets/models/m1911/m1911.fbx");
m_MeshMaterial = Prism::CreateRef<Prism::MaterialInstance>(m_Mesh->GetMaterial());
m_SphereMesh = Prism::CreateRef<Prism::Mesh>("assets/models/Sphere1m.fbx");
m_PlaneMesh = Prism::CreateRef<Prism::Mesh>("assets/models/Plane1m.fbx");
m_GridShader = Prism::Shader::Create("assets/shaders/Grid.glsl");
m_GridMaterial = Prism::MaterialInstance::Create(Prism::Material::Create(m_GridShader));
m_GridMaterial->Set("u_Scale", m_GridScale);
m_GridMaterial->Set("u_Res", m_GridSize);
// Editor
m_CheckerboardTex.reset(Prism::Texture2D::Create("assets/editor/Checkerboard.tga"));
// Environment
m_EnvironmentCubeMap.reset(Prism::TextureCube::Create("assets/textures/environments/Arches_E_PineTree_Radiance.tga"));
m_EnvironmentIrradiance.reset(Prism::TextureCube::Create("assets/textures/environments/Arches_E_PineTree_Irradiance.tga"));
m_BRDFLUT.reset(Prism::Texture2D::Create("assets/textures/BRDF_LUT.tga"));
float x = -4.0f;
float roughness = 0.0f;
for (int i = 0; i < 8; i++)
{
Prism::Ref<Prism::MaterialInstance> mi = Prism::CreateRef<Prism::MaterialInstance>(m_SphereMesh->GetMaterial());
mi->Set("u_Metalness", 1.0f);
mi->Set("u_Roughness", roughness);
mi->Set("u_ModelMatrix", glm::translate(glm::mat4(1.0f), glm::vec3(x, 0.0f, 0.0f)));
x += 1.1f;
roughness += 0.15f;
m_MetalSphereMaterialInstances.push_back(mi);
}
x = -4.0f;
roughness = 0.0f;
for (int i = 0; i < 8; i++)
{
Prism::Ref<Prism::MaterialInstance> mi = Prism::CreateRef<Prism::MaterialInstance>(m_SphereMesh->GetMaterial());
mi->Set("u_Metalness", 0.0f);
mi->Set("u_Roughness", roughness);
mi->Set("u_ModelMatrix", translate(glm::mat4(1.0f), glm::vec3(x, 1.2f, 0.0f)));
x += 1.1f;
roughness += 0.15f;
m_DielectricSphereMaterialInstances.push_back(mi);
}
// Create Quad
x = -1;
float y = -1;
float width = 2, height = 2;
struct QuadVertex
{
glm::vec3 Position;
glm::vec2 TexCoord;
};
QuadVertex* data = new QuadVertex[4];
data[0].Position = glm::vec3(x, y, 0);
data[0].TexCoord = glm::vec2(0, 0);
data[1].Position = glm::vec3(x + width, y, 0);
data[1].TexCoord = glm::vec2(1, 0);
data[2].Position = glm::vec3(x + width, y + height, 0);
data[2].TexCoord = glm::vec2(1, 1);
data[3].Position = glm::vec3(x, y + height, 0);
data[3].TexCoord = glm::vec2(0, 1);
m_VertexBuffer.reset(Prism::VertexBuffer::Create());
m_VertexBuffer->SetData(data, 4 * sizeof(QuadVertex));
uint32_t* indices = new uint32_t[6] { 0, 1, 2, 2, 3, 0, };
m_IndexBuffer.reset(Prism::IndexBuffer::Create());
m_IndexBuffer->SetData(indices, 6 * sizeof(uint32_t));
m_Light.Direction = { -0.5f, -0.5f, 1.0f };
m_Light.Radiance = { 1.0f, 1.0f, 1.0f };
}
void DemoLayer::OnDetach()
{
}
void DemoLayer::OnUpdate(const Prism::TimeStep deltaTime)
{
{
// THINGS TO LOOK AT:
// - BRDF LUT
// - Cubemap mips and filtering
// - Tonemapping and proper HDR pipeline
using namespace Prism;
using namespace glm;
m_Camera.Update(deltaTime);
auto viewProjection = m_Camera.GetProjectionMatrix() * m_Camera.GetViewMatrix();
m_Framebuffer->Bind();
Renderer::Clear(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]);
m_QuadShader->Bind();
m_QuadShader->SetMat4("u_InverseVP", inverse(viewProjection));
m_EnvironmentCubeMap->Bind(0);
m_VertexBuffer->Bind();
m_IndexBuffer->Bind();
Renderer::DrawIndexed(m_IndexBuffer->GetCount(), false);
m_MeshMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color);
m_MeshMaterial->Set("u_Metalness", m_MetalnessInput.Value);
m_MeshMaterial->Set("u_Roughness", m_RoughnessInput.Value);
m_MeshMaterial->Set("u_ViewProjectionMatrix", viewProjection);
m_MeshMaterial->Set("u_ModelMatrix", scale(mat4(1.0f), vec3(m_MeshScale)));
m_MeshMaterial->Set("lights", m_Light);
m_MeshMaterial->Set("u_CameraPosition", m_Camera.GetPosition());
m_MeshMaterial->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f);
m_MeshMaterial->Set("u_EnvMapRotation", m_EnvMapRotation);
m_MeshMaterial->Set("u_EnvRadianceTex", m_EnvironmentCubeMap);
m_MeshMaterial->Set("u_EnvIrradianceTex", m_EnvironmentIrradiance);
m_MeshMaterial->Set("u_BRDFLUTTexture", m_BRDFLUT);
m_SphereMesh->GetMaterial()->Set("u_AlbedoColor", m_AlbedoInput.Color);
m_SphereMesh->GetMaterial()->Set("u_Metalness", m_MetalnessInput.Value);
m_SphereMesh->GetMaterial()->Set("u_Roughness", m_RoughnessInput.Value);
m_SphereMesh->GetMaterial()->Set("u_ViewProjectionMatrix", viewProjection);
m_SphereMesh->GetMaterial()->Set("u_ModelMatrix", scale(mat4(1.0f), vec3(m_MeshScale)));
m_SphereMesh->GetMaterial()->Set("lights", m_Light);
m_SphereMesh->GetMaterial()->Set("u_CameraPosition", m_Camera.GetPosition());
m_SphereMesh->GetMaterial()->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f);
m_SphereMesh->GetMaterial()->Set("u_EnvMapRotation", m_EnvMapRotation);
m_SphereMesh->GetMaterial()->Set("u_EnvRadianceTex", m_EnvironmentCubeMap);
m_SphereMesh->GetMaterial()->Set("u_EnvIrradianceTex", m_EnvironmentIrradiance);
m_SphereMesh->GetMaterial()->Set("u_BRDFLUTTexture", m_BRDFLUT);
if (m_AlbedoInput.TextureMap)
m_MeshMaterial->Set("u_AlbedoTexture", m_AlbedoInput.TextureMap);
if (m_NormalInput.TextureMap)
m_MeshMaterial->Set("u_NormalTexture", m_NormalInput.TextureMap);
if (m_MetalnessInput.TextureMap)
m_MeshMaterial->Set("u_MetalnessTexture", m_MetalnessInput.TextureMap);
if (m_RoughnessInput.TextureMap)
m_MeshMaterial->Set("u_RoughnessTexture", m_RoughnessInput.TextureMap);
if (m_Scene == Scene::Spheres)
{
// Metals
for (int i = 0; i < 8; i++)
{
m_SphereMesh->Render(deltaTime, glm::mat4(1.0f), m_MetalSphereMaterialInstances[i]);
/*
m_MetalSphereMaterialInstances[i]->Bind();
m_SphereMesh->Render(deltaTime, m_SimplePBRShader.get());
*/
}
// Dielectrics
for (int i = 0; i < 8; i++)
{
m_SphereMesh->Render(deltaTime, glm::mat4(1.0f), m_DielectricSphereMaterialInstances[i]);
/*
m_DielectricSphereMaterialInstances[i]->Bind();
m_SphereMesh->Render(deltaTime, m_SimplePBRShader.get());
*/
}
}
else if (m_Scene == Scene::Model)
{
if (m_Mesh)
{
m_Mesh->Render(deltaTime, scale(mat4(1.0f), vec3(m_MeshScale)), m_MeshMaterial);
}
}
m_GridMaterial->Set("u_MVP", viewProjection * glm::scale(glm::mat4(1.0f), glm::vec3(16.0f)));
m_PlaneMesh->Render(deltaTime, m_GridMaterial);
m_Framebuffer->Unbind();
m_FinalPresentBuffer->Bind();
m_HDRShader->Bind();
m_HDRShader->SetFloat("u_Exposure", m_Exposure);
m_Framebuffer->BindTexture();
m_VertexBuffer->Bind();
m_IndexBuffer->Bind();
Renderer::DrawIndexed(m_IndexBuffer->GetCount(), false);
m_FinalPresentBuffer->Unbind();
}
}
void DemoLayer::OnImGuiRender()
{
#define ENABLE_DOCKSPACE 1
#if ENABLE_DOCKSPACE
static bool p_open = true;
static bool opt_fullscreen = true;
static bool opt_padding = false;
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
// because it would be confusing to have two docking targets within each others.
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
if (opt_fullscreen)
{
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
}
else
{
dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
}
// When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background
// and handle the pass-thru hole, so we ask Begin() to not render a background.
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
window_flags |= ImGuiWindowFlags_NoBackground;
// Important: note that we proceed even if Begin() returns false (aka window is collapsed).
// This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
// all active windows docked into it will lose their parent and become undocked.
// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
if (!opt_padding)
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("DockSpace Demo", &p_open, window_flags);
if (!opt_padding)
ImGui::PopStyleVar();
if (opt_fullscreen)
ImGui::PopStyleVar(2);
// Submit the DockSpace
// REMINDER: THIS IS A DEMO FOR ADVANCED USAGE OF DockSpace()!
// MOST REGULAR APPLICATIONS WILL SIMPLY WANT TO CALL DockSpaceOverViewport(). READ COMMENTS ABOVE.
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
{
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
}
// Show demo options and help
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Options"))
{
// Disabling fullscreen would allow the window to be moved to the front of other windows,
// which we can't undo at the moment without finer window depth/z control.
ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen);
ImGui::MenuItem("Padding", NULL, &opt_padding);
ImGui::Separator();
if (ImGui::MenuItem("Flag: NoDockingOverCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingOverCentralNode; }
if (ImGui::MenuItem("Flag: NoDockingSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingSplit) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingSplit; }
if (ImGui::MenuItem("Flag: NoUndocking", "", (dockspace_flags & ImGuiDockNodeFlags_NoUndocking) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoUndocking; }
if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoResize; }
if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; }
if (ImGui::MenuItem("Flag: PassthruCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0, opt_fullscreen)) { dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode; }
ImGui::Separator();
if (ImGui::MenuItem("Close", NULL, false, p_open != false))
p_open = false;
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Help"))
{
ImGui::TextUnformatted(
"This demo has nothing to do with enabling docking!" "\n"
"This demo only demonstrate the use of ImGui::DockSpace() which allows you to manually\ncreate a docking node _within_ another window." "\n"
"Most application can simply call ImGui::DockSpaceOverViewport() and be done with it.");
ImGui::Separator();
ImGui::TextUnformatted("When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n"
"- Drag from window title bar or their tab to dock/undock." "\n"
"- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n"
"- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n"
"- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)");
ImGui::Separator();
ImGui::TextUnformatted("More details:"); ImGui::Bullet(); ImGui::SameLine(); ImGui::TextLinkOpenURL("Docking Wiki page", "https://github.com/ocornut/imgui/wiki/Docking");
ImGui::BulletText("Read comments in ShowExampleAppDockSpace()");
ImGui::EndMenu();
}
ImGuiShowHelpMarker(
"You can _always_ dock _any_ window into another by holding the SHIFT key while moving a window. Try it now!" "\n"
"This demo app has nothing to do with it!" "\n\n"
"This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." "\n\n"
"ImGui::DockSpace() comes with one hard constraint: it needs to be submitted _before_ any window which may be docked into it. Therefore, if you use a dock spot as the central point of your application, you'll probably want it to be part of the very first window you are submitting to imgui every frame." "\n\n"
"(NB: because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node, because that window is submitted as part of the NewFrame() call. An easy workaround is that you can create your own implicit \"Debug##2\" window after calling DockSpace() and leave it in the window stack for anyone to use.)"
);
ImGui::EndMenuBar();
}
ImGui::End();
#endif
// Editor Panel ------------------------------------------------------------------------------
ImGui::Begin("Model");
ImGui::RadioButton("Spheres", (int*)&m_Scene, (int)Scene::Spheres);
ImGui::SameLine();
ImGui::RadioButton("Model", (int*)&m_Scene, (int)Scene::Model);
ImGui::Begin("Environment");
ImGui::Columns(2);
ImGui::AlignTextToFramePadding();
Property("Light Direction", m_Light.Direction);
Property("Light Radiance", m_Light.Radiance, PropertyFlag::ColorProperty);
Property("Light Multiplier", m_LightMultiplier, 0.0f, 5.0f);
Property("Exposure", m_Exposure, 0.0f, 5.0f);
Property("Radiance Prefiltering", m_RadiancePrefilter);
Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f);
ImGui::Columns(1);
ImGui::End();
ImGui::Separator();
{
ImGui::Text("Mesh");
std::string fullpath = m_Mesh ? m_Mesh->GetFilePath() : "None";
size_t found = fullpath.find_last_of("/\\");
std::string path = found != std::string::npos ? fullpath.substr(found + 1) : fullpath;
ImGui::Text(path.c_str()); ImGui::SameLine();
if (ImGui::Button("...##Mesh"))
{
std::string filename = Prism::Application::Get().OpenFile("");
if (filename != "")
m_Mesh.reset(new Prism::Mesh(filename));
}
}
ImGui::Separator();
// Textures ------------------------------------------------------------------------------
{
// Albedo
if (ImGui::CollapsingHeader("Albedo", nullptr, ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10));
ImGui::Image((ImTextureRef)(m_AlbedoInput.TextureMap ? m_AlbedoInput.TextureMap->GetRendererID() : m_CheckerboardTex->GetRendererID()), ImVec2(64, 64));
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
if (m_AlbedoInput.TextureMap)
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(m_AlbedoInput.TextureMap->GetPath().c_str());
ImGui::PopTextWrapPos();
ImGui::Image((ImTextureRef)m_AlbedoInput.TextureMap->GetRendererID(), ImVec2(384, 384));
ImGui::EndTooltip();
}
if (ImGui::IsItemClicked())
{
std::string filename = Prism::Application::Get().OpenFile("");
if (filename != "")
m_AlbedoInput.TextureMap.reset(Prism::Texture2D::Create(filename, m_AlbedoInput.SRGB));
}
}
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Checkbox("Use##AlbedoMap", &m_AlbedoInput.UseTexture);
if (ImGui::Checkbox("sRGB##AlbedoMap", &m_AlbedoInput.SRGB))
{
if (m_AlbedoInput.TextureMap)
m_AlbedoInput.TextureMap.reset(Prism::Texture2D::Create(m_AlbedoInput.TextureMap->GetPath(), m_AlbedoInput.SRGB));
}
ImGui::EndGroup();
ImGui::SameLine();
ImGui::ColorEdit3("Color##Albedo", glm::value_ptr(m_AlbedoInput.Color), ImGuiColorEditFlags_NoInputs);
}
}
{
// Normals
if (ImGui::CollapsingHeader("Normals", nullptr, ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10));
ImGui::Image((ImTextureRef)(m_NormalInput.TextureMap ? m_NormalInput.TextureMap->GetRendererID() : m_CheckerboardTex->GetRendererID()), ImVec2(64, 64));
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
if (m_NormalInput.TextureMap)
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(m_NormalInput.TextureMap->GetPath().c_str());
ImGui::PopTextWrapPos();
ImGui::Image((ImTextureRef)m_NormalInput.TextureMap->GetRendererID(), ImVec2(384, 384));
ImGui::EndTooltip();
}
if (ImGui::IsItemClicked())
{
std::string filename = Prism::Application::Get().OpenFile("");
if (filename != "")
m_NormalInput.TextureMap.reset(Prism::Texture2D::Create(filename));
}
}
ImGui::SameLine();
ImGui::Checkbox("Use##NormalMap", &m_NormalInput.UseTexture);
}
}
{
// Metalness
if (ImGui::CollapsingHeader("Metalness", nullptr, ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10));
ImGui::Image((ImTextureRef)(m_MetalnessInput.TextureMap ? m_MetalnessInput.TextureMap->GetRendererID() : m_CheckerboardTex->GetRendererID()), ImVec2(64, 64));
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
if (m_MetalnessInput.TextureMap)
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(m_MetalnessInput.TextureMap->GetPath().c_str());
ImGui::PopTextWrapPos();
ImGui::Image((ImTextureRef)m_MetalnessInput.TextureMap->GetRendererID(), ImVec2(384, 384));
ImGui::EndTooltip();
}
if (ImGui::IsItemClicked())
{
std::string filename = Prism::Application::Get().OpenFile("");
if (filename != "")
m_MetalnessInput.TextureMap.reset(Prism::Texture2D::Create(filename));
}
}
ImGui::SameLine();
ImGui::Checkbox("Use##MetalnessMap", &m_MetalnessInput.UseTexture);
ImGui::SameLine();
ImGui::SliderFloat("Value##MetalnessInput", &m_MetalnessInput.Value, 0.0f, 1.0f);
}
}
{
// Roughness
if (ImGui::CollapsingHeader("Roughness", nullptr, ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10));
ImGui::Image((ImTextureRef)(m_RoughnessInput.TextureMap ? m_RoughnessInput.TextureMap->GetRendererID() : m_CheckerboardTex->GetRendererID()), ImVec2(64, 64));
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
if (m_RoughnessInput.TextureMap)
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(m_RoughnessInput.TextureMap->GetPath().c_str());
ImGui::PopTextWrapPos();
ImGui::Image((ImTextureRef)m_RoughnessInput.TextureMap->GetRendererID(), ImVec2(384, 384));
ImGui::EndTooltip();
}
if (ImGui::IsItemClicked())
{
std::string filename = Prism::Application::Get().OpenFile("");
if (filename != "")
m_RoughnessInput.TextureMap.reset(Prism::Texture2D::Create(filename));
}
}
ImGui::SameLine();
ImGui::Checkbox("Use##RoughnessMap", &m_RoughnessInput.UseTexture);
ImGui::SameLine();
ImGui::SliderFloat("Value##RoughnessInput", &m_RoughnessInput.Value, 0.0f, 1.0f);
}
}
if (ImGui::TreeNode("Shaders"))
{
auto& shaders = Prism::Shader::GetAllShaders();
for (auto& shader : shaders)
{
if (ImGui::TreeNode(shader->GetName().c_str()))
{
std::string buttonName = "Reload##" + shader->GetName();
if (ImGui::Button(buttonName.c_str()))
shader->Reload();
ImGui::TreePop();
}
}
ImGui::TreePop();
}
ImGui::Separator();
ImGui::End();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::Begin("Viewport");
auto viewportSize = ImGui::GetContentRegionAvail();
m_Framebuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
m_FinalPresentBuffer->Resize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
m_Camera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f));
ImGui::Image((ImTextureRef)m_FinalPresentBuffer->GetColorAttachmentRendererID(), viewportSize, { 0, 1 }, { 1, 0 });
ImGui::End();
ImGui::PopStyleVar();
ImGui::Begin("Log");
const auto& cameraPos = m_Camera.GetPosition();
ImGui::Text("cameraPos: (%.2f, %.2f, %.2f)", cameraPos.x, cameraPos.y, cameraPos.z);
const auto& Direct = m_Camera.GetForwardDirection();
ImGui::Text("forward Vec: (%.2f, %.2f, %.2f)", Direct.x, Direct.y, Direct.z);
const auto& distance = m_Camera.GetDistance();
ImGui::Text("distance: %.3f", distance);
ImGui::End();
if (m_Mesh)
m_Mesh->OnImGuiRender();
}
void DemoLayer::OnEvent(Prism::Event& e)
{
Layer::OnEvent(e);
}

View File

@ -1,115 +0,0 @@
//
// Created by sfd on 25-11-21.
//
#ifndef DEMOLAYER_H
#define DEMOLAYER_H
#include "Prism.h"
#include "Prism/Renderer/Camera.h"
#include "Prism/Renderer/FrameBuffer.h"
#include "Prism/Renderer/Material.h"
#include "Prism/Renderer/Mesh.h"
class DemoLayer : public Prism::Layer
{
public:
DemoLayer();
virtual ~DemoLayer();
virtual void OnAttach() override;
virtual void OnDetach() override;
virtual void OnUpdate(Prism::TimeStep deltaTime) override;
virtual void OnImGuiRender() override;
virtual void OnEvent(Prism::Event& e) override;
private:
float m_ClearColor[4];
Prism::Ref<Prism::Shader> m_QuadShader;
Prism::Ref<Prism::Shader> m_HDRShader;
Prism::Ref<Prism::Shader> m_GridShader;
Prism::Ref<Prism::Mesh> m_Mesh;
Prism::Ref<Prism::Mesh> m_SphereMesh, m_PlaneMesh;
Prism::Ref<Prism::Texture2D> m_BRDFLUT;
Prism::Ref<Prism::MaterialInstance> m_MeshMaterial;
Prism::Ref<Prism::MaterialInstance> m_GridMaterial;
std::vector<Prism::Ref<Prism::MaterialInstance>> m_MetalSphereMaterialInstances;
std::vector<Prism::Ref<Prism::MaterialInstance>> m_DielectricSphereMaterialInstances;
float m_GridScale = 16.025f, m_GridSize = 0.025f;
float m_MeshScale = 1.0f;
Prism::Ref<Prism::Shader> m_Shader;
Prism::Ref<Prism::Shader> m_PBRShader;
struct AlbedoInput
{
glm::vec3 Color = { 0.972f, 0.96f, 0.915f }; // Silver, from https://docs.unrealengine.com/en-us/Engine/Rendering/Materials/PhysicallyBased
Prism::Ref<Prism::Texture2D> TextureMap;
bool SRGB = true;
bool UseTexture = false;
};
AlbedoInput m_AlbedoInput;
struct NormalInput
{
Prism::Ref<Prism::Texture2D> TextureMap;
bool UseTexture = false;
};
NormalInput m_NormalInput;
struct MetalnessInput
{
float Value = 1.0f;
Prism::Ref<Prism::Texture2D> TextureMap;
bool UseTexture = false;
};
MetalnessInput m_MetalnessInput;
struct RoughnessInput
{
float Value = 0.5f;
Prism::Ref<Prism::Texture2D> TextureMap;
bool UseTexture = false;
};
RoughnessInput m_RoughnessInput;
Prism::Ref<Prism::FrameBuffer> m_Framebuffer, m_FinalPresentBuffer;
Prism::Ref<Prism::VertexBuffer> m_VertexBuffer;
Prism::Ref<Prism::IndexBuffer> m_IndexBuffer;
Prism::Ref<Prism::TextureCube> m_EnvironmentCubeMap, m_EnvironmentIrradiance;
Prism::Camera m_Camera;
struct Light
{
glm::vec3 Direction;
glm::vec3 Radiance;
};
Light m_Light;
float m_LightMultiplier = 0.3f;
// PBR params
float m_Exposure = 1.0f;
bool m_RadiancePrefilter = false;
float m_EnvMapRotation = 0.0f;
enum class Scene : uint32_t
{
Spheres = 0, Model = 1
};
Scene m_Scene;
// Editor resources
Prism::Ref<Prism::Texture2D> m_CheckerboardTex;
};
#endif //DEMOLAYER_H

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Binary file not shown.

View File

@ -1,23 +0,0 @@
# Blender v2.78 (sub 0) OBJ File: ''
# www.blender.org
v 10.000000 -10.000000 -9.999999
v 10.000000 -10.000000 10.000000
v -10.000001 -10.000000 9.999998
v -9.999996 -10.000000 -10.000004
v 10.000005 10.000000 -9.999994
v 9.999993 10.000000 10.000006
v -10.000004 10.000000 9.999996
v -9.999999 10.000000 -10.000000
vn 0.0000 1.0000 -0.0000
vn 0.0000 -1.0000 0.0000
vn -1.0000 -0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn -0.0000 -0.0000 1.0000
s off
f 1//1 4//1 3//1 2//1
f 5//2 6//2 7//2 8//2
f 1//3 2//3 6//3 5//3
f 2//4 3//4 7//4 6//4
f 3//5 4//5 8//5 7//5
f 5//6 8//6 4//6 1//6

Binary file not shown.

Binary file not shown.

View File

@ -1,17 +0,0 @@
# Blender v2.80 (sub 75) OBJ File: 'Plane1m.blend'
# www.blender.org
mtllib Plane1m.mtl
o Plane
v -0.500000 0.000000 0.500000
v 0.500000 0.000000 0.500000
v -0.500000 0.000000 -0.500000
v 0.500000 0.000000 -0.500000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 1.000000
vn 0.0000 1.0000 0.0000
usemtl None
s off
f 2/1/1 3/2/1 1/3/1
f 2/1/1 4/4/1 3/2/1

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

View File

@ -1,49 +0,0 @@
// Simple Texture Shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 4) in vec2 a_TexCoord;
uniform mat4 u_MVP;
out vec2 v_TexCoord;
void main()
{
vec4 position = u_MVP * vec4(a_Position, 1.0);
gl_Position = position;
v_TexCoord = a_TexCoord;
}
#type fragment
#version 430
layout(location = 0) out vec4 color;
uniform float u_Scale;
uniform float u_Res;
in vec2 v_TexCoord;
/*void main()
{
color = texture(u_Texture, v_TexCoord * 8.0);
}*/
float grid(vec2 st, float res)
{
vec2 grid = fract(st);
return step(res, grid.x) * step(res, grid.y);
}
void main()
{
float scale = u_Scale;
float resolution = u_Res;
float x = grid(v_TexCoord * scale, resolution);
color = vec4(vec3(0.2), 0.5) * (1.0 - x);
}

View File

@ -1,29 +0,0 @@
#type vertex
#version 430 core
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec3 a_Normal;
uniform mat4 u_MVP;
out vec3 v_Normal;
void main()
{
gl_Position = u_MVP * vec4(a_Position, 1.0);
v_Normal = a_Normal;
}
#type fragment
#version 430 core
layout(location = 0) out vec4 o_Color;
uniform vec4 u_Color;
in vec3 v_Normal;
void main()
{
o_Color = u_Color * vec4(v_Normal * 0.5 + 0.5, 1.0f);
// o_Color = vec4(0.6, 0.7, 0.8, 1.0);
}

View File

@ -1,43 +0,0 @@
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
vec4 position = vec4(a_Position.xy, 1.0, 1.0);
v_TexCoord = a_TexCoord;
gl_Position = position;
}
#type fragment
#version 430
in vec2 v_TexCoord;
uniform sampler2D u_Texture;
layout(location=0) out vec4 outColor;
uniform float u_Exposure;
void main()
{
const float gamma = 2.2;
const float pureWhite = 1.0;
vec3 color = texture(u_Texture, v_TexCoord).rgb * u_Exposure;
// Reinhard tonemapping operator.
// see: "Photographic Tone Reproduction for Digital Images", eq. 4
float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
float mappedLuminance = (luminance * (1.0 + luminance / (pureWhite * pureWhite))) / (1.0 + luminance);
// Scale color by ratio of average luminances.
vec3 mappedColor = (mappedLuminance / luminance) * color;
// Gamma correction.
outColor = vec4(pow(mappedColor, vec3(1.0/gamma)), 1.0);
}

View File

@ -1,33 +0,0 @@
// Simple Textured Quad Shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
uniform mat4 u_InverseVP;
out vec3 v_Position;
void main()
{
vec4 position = vec4(a_Position.xy, 1.0, 1.0);
gl_Position = position;
v_Position = (u_InverseVP * position).xyz;
}
#type fragment
#version 430
layout(location = 0) out vec4 finalColor;
uniform samplerCube u_Texture;
in vec3 v_Position;
void main()
{
finalColor = textureLod(u_Texture, v_Position, 0.0);
}

View File

@ -1,32 +0,0 @@
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec3 a_Normal;
layout(location = 2) in vec3 a_Tangent;
layout(location = 3) in vec3 a_Bitangent;
layout(location = 4) in vec2 a_TexCoord;
uniform mat4 u_MVP;
out vec3 v_Normal;
void main()
{
gl_Position = u_MVP * vec4(a_Position, 1.0);
v_Normal = a_Normal;
}
#type fragment
#version 430
layout(location = 0) out vec4 finalColor;
//uniform vec4 u_Color;
in vec3 v_Normal;
void main()
{
finalColor = vec4((v_Normal * 0.5 + 0.5), 1.0);// * u_Color.xyz, 1.0);
}

View File

@ -1,336 +0,0 @@
// -----------------------------
// -- Hazel Engine PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
// Currently heavily updated.
//
// References upon which this is based:
// - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf)
// - Frostbite's SIGGRAPH 2014 paper (https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/)
// - Michał Siejak's PBR project (https://github.com/Nadrin)
// - My implementation from years ago in the Sparky engine (https://github.com/TheCherno/Sparky)
#type vertex
#version 430 core
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec3 a_Normal;
layout(location = 2) in vec3 a_Tangent;
layout(location = 3) in vec3 a_Binormal;
layout(location = 4) in vec2 a_TexCoord;
layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ModelMatrix;
const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100];
out VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
vec3 Binormal;
} vs_Output;
void main()
{
mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0];
boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1];
boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2];
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
vs_Output.WorldPosition = vec3(u_ModelMatrix * boneTransform * vec4(a_Position, 1.0));
vs_Output.Normal = mat3(boneTransform) * a_Normal;
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y);
vs_Output.WorldNormals = mat3(u_ModelMatrix) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.Binormal = mat3(boneTransform) * a_Binormal;
//gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0);
gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * localPosition;
}
#type fragment
#version 430 core
const float PI = 3.141592;
const float Epsilon = 0.00001;
const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04);
struct Light {
vec3 Direction;
vec3 Radiance;
};
in VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
vec3 Binormal;
} vs_Input;
layout(location=0) out vec4 color;
uniform Light lights;
uniform vec3 u_CameraPosition;
// PBR texture inputs
uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_MetalnessTexture;
uniform sampler2D u_RoughnessTexture;
// Environment maps
uniform samplerCube u_EnvRadianceTex;
uniform samplerCube u_EnvIrradianceTex;
// BRDF LUT
uniform sampler2D u_BRDFLUTTexture;
uniform vec3 u_AlbedoColor;
uniform float u_Metalness;
uniform float u_Roughness;
uniform float u_EnvMapRotation;
// Toggles
uniform float u_RadiancePrefilter;
uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
struct PBRParameters
{
vec3 Albedo;
float Roughness;
float Metalness;
vec3 Normal;
vec3 View;
float NdotV;
};
PBRParameters m_Params;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float NdotV, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k);
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// Shlick's approximation of the Fresnel factor.
vec3 fresnelSchlick(vec3 F0, float cosTheta)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
{
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// ---------------------------------------------------------------------------------------------------
// The following code (from Unreal Engine 4's paper) shows how to filter the environment map
// for different roughnesses. This is mean to be computed offline and stored in cube map mips,
// so turning this on online will cause poor performance
float RadicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N)
{
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
{
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
vec3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
vec3 TangentX = normalize( cross( UpVector, N ) );
vec3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
float TotalWeight = 0.0;
vec3 PrefilterEnvMap(float Roughness, vec3 R)
{
vec3 N = R;
vec3 V = R;
vec3 PrefilteredColor = vec3(0.0);
int NumSamples = 1024;
for(int i = 0; i < NumSamples; i++)
{
vec2 Xi = Hammersley(i, NumSamples);
vec3 H = ImportanceSampleGGX(Xi, Roughness, N);
vec3 L = 2 * dot(V, H) * H - V;
float NoL = clamp(dot(N, L), 0.0, 1.0);
if (NoL > 0)
{
PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL;
TotalWeight += NoL;
}
}
return PrefilteredColor / TotalWeight;
}
// ---------------------------------------------------------------------------------------------------
vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
vec3(0.0,1.0,0.0),
vec3(-sin(angle),0.0,cos(angle))};
return rotationMatrix * vec;
}
vec3 Lighting(vec3 F0)
{
vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++)
{
vec3 Li = -lights.Direction;
vec3 Lradiance = lights.Radiance;
vec3 Lh = normalize(Li + m_Params.View);
// Calculate angles between surface normal and various light vectors.
float cosLi = max(0.0, dot(m_Params.Normal, Li));
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
float D = ndfGGX(cosLh, m_Params.Roughness);
float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseBRDF = kd * m_Params.Albedo;
// Cook-Torrance
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
return result;
}
vec3 IBL(vec3 F0, vec3 Lr)
{
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseIBL = m_Params.Albedo * irradiance;
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
vec3 specularIrradiance = vec3(0.0);
if (u_RadiancePrefilter > 0.5)
specularIrradiance = PrefilterEnvMap(m_Params.Roughness * m_Params.Roughness, R) * u_RadiancePrefilter;
else
specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), sqrt(m_Params.Roughness) * u_EnvRadianceTexLevels).rgb * (1.0 - u_RadiancePrefilter);
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
return kd * diffuseIBL + specularIBL;
}
void main()
{
// Standard PBR inputs
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
// Normals (either from vertex or map)
m_Params.Normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
{
m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0);
m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal);
}
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
// Specular reflection vector
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
// Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0);
vec3 iblContribution = IBL(F0, Lr);
color = vec4(lightContribution + iblContribution, 1.0);
}

View File

@ -1,321 +0,0 @@
// -----------------------------
// -- Hazel Engine PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
// Currently heavily updated.
//
// References upon which this is based:
// - Unreal Engine 4 PBR notes (https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf)
// - Frostbite's SIGGRAPH 2014 paper (https://seblagarde.wordpress.com/2015/07/14/siggraph-2014-moving-frostbite-to-physically-based-rendering/)
// - Michał Siejak's PBR project (https://github.com/Nadrin)
// - My implementation from years ago in the Sparky engine (https://github.com/TheCherno/Sparky)
#type vertex
#version 430 core
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec3 a_Normal;
layout(location = 2) in vec3 a_Tangent;
layout(location = 3) in vec3 a_Binormal;
layout(location = 4) in vec2 a_TexCoord;
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ModelMatrix;
out VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
vec3 Binormal;
} vs_Output;
void main()
{
vs_Output.WorldPosition = vec3(u_ModelMatrix * vec4(a_Position, 1.0));
vs_Output.Normal = a_Normal;
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y);
vs_Output.WorldNormals = mat3(u_ModelMatrix) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.Binormal = a_Binormal;
gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0);
}
#type fragment
#version 430 core
const float PI = 3.141592;
const float Epsilon = 0.00001;
const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04);
struct Light {
vec3 Direction;
vec3 Radiance;
};
in VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
vec3 Binormal;
} vs_Input;
layout(location = 0) out vec4 color;
uniform Light lights;
uniform vec3 u_CameraPosition;
// PBR texture inputs
uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_MetalnessTexture;
uniform sampler2D u_RoughnessTexture;
// Environment maps
uniform samplerCube u_EnvRadianceTex;
uniform samplerCube u_EnvIrradianceTex;
// BRDF LUT
uniform sampler2D u_BRDFLUTTexture;
uniform vec3 u_AlbedoColor;
uniform float u_Metalness;
uniform float u_Roughness;
uniform float u_EnvMapRotation;
// Toggles
uniform float u_RadiancePrefilter;
uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
struct PBRParameters
{
vec3 Albedo;
float Roughness;
float Metalness;
vec3 Normal;
vec3 View;
float NdotV;
};
PBRParameters m_Params;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float NdotV, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k);
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// Shlick's approximation of the Fresnel factor.
vec3 fresnelSchlick(vec3 F0, float cosTheta)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
{
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// ---------------------------------------------------------------------------------------------------
// The following code (from Unreal Engine 4's paper) shows how to filter the environment map
// for different roughnesses. This is mean to be computed offline and stored in cube map mips,
// so turning this on online will cause poor performance
float RadicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N)
{
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
{
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
vec3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
vec3 TangentX = normalize( cross( UpVector, N ) );
vec3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
float TotalWeight = 0.0;
vec3 PrefilterEnvMap(float Roughness, vec3 R)
{
vec3 N = R;
vec3 V = R;
vec3 PrefilteredColor = vec3(0.0);
int NumSamples = 1024;
for(int i = 0; i < NumSamples; i++)
{
vec2 Xi = Hammersley(i, NumSamples);
vec3 H = ImportanceSampleGGX(Xi, Roughness, N);
vec3 L = 2 * dot(V, H) * H - V;
float NoL = clamp(dot(N, L), 0.0, 1.0);
if (NoL > 0)
{
PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL;
TotalWeight += NoL;
}
}
return PrefilteredColor / TotalWeight;
}
// ---------------------------------------------------------------------------------------------------
vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
vec3(0.0,1.0,0.0),
vec3(-sin(angle),0.0,cos(angle))};
return rotationMatrix * vec;
}
vec3 Lighting(vec3 F0)
{
vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++)
{
vec3 Li = -lights.Direction;
vec3 Lradiance = lights.Radiance;
vec3 Lh = normalize(Li + m_Params.View);
// Calculate angles between surface normal and various light vectors.
float cosLi = max(0.0, dot(m_Params.Normal, Li));
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
float D = ndfGGX(cosLh, m_Params.Roughness);
float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseBRDF = kd * m_Params.Albedo;
// Cook-Torrance
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
return result;
}
vec3 IBL(vec3 F0, vec3 Lr)
{
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseIBL = m_Params.Albedo * irradiance;
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
vec3 specularIrradiance = vec3(0.0);
if (u_RadiancePrefilter > 0.5)
specularIrradiance = PrefilterEnvMap(m_Params.Roughness * m_Params.Roughness, R) * u_RadiancePrefilter;
else
specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), sqrt(m_Params.Roughness) * u_EnvRadianceTexLevels).rgb * (1.0 - u_RadiancePrefilter);
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
return kd * diffuseIBL + specularIBL;
}
void main()
{
// Standard PBR inputs
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
// Normals (either from vertex or map)
m_Params.Normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
{
m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0);
m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal);
}
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
// Specular reflection vector
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
// Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0);
vec3 iblContribution = IBL(F0, Lr);
color = vec4(lightContribution + iblContribution, 1.0);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 MiB