add simple render, add simple pbr shader(from hazel)
@ -8,5 +8,5 @@ file(COPY ${ASSETS} DESTINATION ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SRC_SOURCE})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Prism)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Prism-static)
|
||||
#target_compile_definitions(${PROJECT_NAME} PRIVATE ENABLE_DOCKSPACE)
|
||||
@ -21,6 +21,8 @@ static void ImGuiShowHelpMarker(const char* desc)
|
||||
}
|
||||
|
||||
DemoLayer::DemoLayer()
|
||||
: m_ClearColor{ 0.1f, 0.1f, 0.1f, 1.0f }, m_Scene(Scene::Spheres),
|
||||
m_Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f))
|
||||
{
|
||||
}
|
||||
|
||||
@ -30,24 +32,55 @@ DemoLayer::~DemoLayer()
|
||||
|
||||
void DemoLayer::OnAttach()
|
||||
{
|
||||
static float vertices[] = {
|
||||
-0.5f, -0.5f, 0.0f,
|
||||
0.5f, -0.5f, 0.0f,
|
||||
0.0f, 0.5f, 0.0f
|
||||
};
|
||||
m_SimplePBRShader.reset(Prism::Shader::Create("assets/shaders/simplepbr.glsl"));
|
||||
m_QuadShader.reset(Prism::Shader::Create("assets/shaders/quad.glsl"));
|
||||
m_HDRShader.reset(Prism::Shader::Create("assets/shaders/hdr.glsl"));
|
||||
m_Mesh.reset(new Prism::Mesh("assets/meshes/cerberus.fbx"));
|
||||
m_SphereMesh.reset(new Prism::Mesh("assets/models/Sphere.fbx"));
|
||||
|
||||
static unsigned int indices[] = {
|
||||
0, 1, 2
|
||||
};
|
||||
// Editor
|
||||
m_CheckerboardTex.reset(Prism::Texture2D::Create("assets/editor/Checkerboard.tga"));
|
||||
|
||||
m_VertexBuffer = std::unique_ptr<Prism::VertexBuffer>(Prism::VertexBuffer::Create());
|
||||
m_VertexBuffer->SetData(vertices, sizeof(vertices));
|
||||
// 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"));
|
||||
|
||||
m_IndexBuffer = std::unique_ptr<Prism::IndexBuffer>(Prism::IndexBuffer::Create());
|
||||
m_IndexBuffer->SetData(indices, sizeof(indices));
|
||||
m_Framebuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA16F));
|
||||
m_FinalPresentBuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA8));
|
||||
|
||||
// Create Quad
|
||||
float x = -1, y = -1;
|
||||
float width = 2, height = 2;
|
||||
struct QuadVertex
|
||||
{
|
||||
glm::vec3 Position;
|
||||
glm::vec2 TexCoord;
|
||||
};
|
||||
|
||||
m_Shader.reset(Prism::Shader::Create("assets/shaders/shader.glsl"));
|
||||
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(unsigned int));
|
||||
|
||||
m_Light.Direction = { -0.5f, -0.5f, 1.0f };
|
||||
m_Light.Radiance = { 1.0f, 1.0f, 1.0f };
|
||||
}
|
||||
|
||||
void DemoLayer::OnDetach()
|
||||
@ -56,28 +89,114 @@ void DemoLayer::OnDetach()
|
||||
|
||||
void DemoLayer::OnUpdate()
|
||||
{
|
||||
Prism::Renderer::Clear(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]);
|
||||
{
|
||||
// THINGS TO LOOK AT:
|
||||
// - BRDF LUT
|
||||
// - Cubemap mips and filtering
|
||||
// - Tonemapping and proper HDR pipeline
|
||||
using namespace Prism;
|
||||
using namespace glm;
|
||||
|
||||
Prism::UniformBufferDeclaration<sizeof(glm::vec4), 1> buffer;
|
||||
buffer.Push("u_Color", m_TriangleColor);
|
||||
m_Shader->UploadUniformBuffer(buffer);
|
||||
m_Camera.Update();
|
||||
auto viewProjection = m_Camera.GetProjectionMatrix() * m_Camera.GetViewMatrix();
|
||||
|
||||
m_Shader->Bind();
|
||||
m_VertexBuffer->Bind();
|
||||
m_IndexBuffer->Bind();
|
||||
Prism::Renderer::DrawIndexed(3);
|
||||
m_Framebuffer->Bind();
|
||||
Renderer::Clear(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]);
|
||||
|
||||
Prism::UniformBufferDeclaration<sizeof(mat4), 1> quadShaderUB;
|
||||
quadShaderUB.Push("u_InverseVP", inverse(viewProjection));
|
||||
m_QuadShader->UploadUniformBuffer(quadShaderUB);
|
||||
|
||||
m_QuadShader->Bind();
|
||||
// m_EnvironmentIrradiance->Bind(0);
|
||||
m_EnvironmentCubeMap->Bind(0);
|
||||
m_VertexBuffer->Bind();
|
||||
m_IndexBuffer->Bind();
|
||||
Renderer::DrawIndexed(m_IndexBuffer->GetCount(), false);
|
||||
|
||||
Prism::UniformBufferDeclaration<sizeof(mat4) * 2 + sizeof(vec3) * 4 + sizeof(float) * 8, 14> simplePbrShaderUB;
|
||||
simplePbrShaderUB.Push("u_ViewProjectionMatrix", viewProjection);
|
||||
simplePbrShaderUB.Push("u_ModelMatrix", mat4(1.0f));
|
||||
simplePbrShaderUB.Push("u_AlbedoColor", m_AlbedoInput.Color);
|
||||
simplePbrShaderUB.Push("u_Metalness", m_MetalnessInput.Value);
|
||||
simplePbrShaderUB.Push("u_Roughness", m_RoughnessInput.Value);
|
||||
simplePbrShaderUB.Push("lights.Direction", m_Light.Direction);
|
||||
simplePbrShaderUB.Push("lights.Radiance", m_Light.Radiance * m_LightMultiplier);
|
||||
simplePbrShaderUB.Push("u_CameraPosition", m_Camera.GetPosition());
|
||||
simplePbrShaderUB.Push("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f);
|
||||
simplePbrShaderUB.Push("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f);
|
||||
simplePbrShaderUB.Push("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f);
|
||||
simplePbrShaderUB.Push("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f);
|
||||
simplePbrShaderUB.Push("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f);
|
||||
simplePbrShaderUB.Push("u_EnvMapRotation", m_EnvMapRotation);
|
||||
m_SimplePBRShader->UploadUniformBuffer(simplePbrShaderUB);
|
||||
|
||||
m_EnvironmentCubeMap->Bind(10);
|
||||
m_EnvironmentIrradiance->Bind(11);
|
||||
m_BRDFLUT->Bind(15);
|
||||
|
||||
m_SimplePBRShader->Bind();
|
||||
if (m_AlbedoInput.TextureMap)
|
||||
m_AlbedoInput.TextureMap->Bind(1);
|
||||
if (m_NormalInput.TextureMap)
|
||||
m_NormalInput.TextureMap->Bind(2);
|
||||
if (m_MetalnessInput.TextureMap)
|
||||
m_MetalnessInput.TextureMap->Bind(3);
|
||||
if (m_RoughnessInput.TextureMap)
|
||||
m_RoughnessInput.TextureMap->Bind(4);
|
||||
|
||||
if (m_Scene == Scene::Spheres)
|
||||
{
|
||||
// Metals
|
||||
float roughness = 0.0f;
|
||||
float x = -88.0f;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
m_SimplePBRShader->SetMat4("u_ModelMatrix", translate(mat4(1.0f), vec3(x, 0.0f, 0.0f)));
|
||||
m_SimplePBRShader->SetFloat("u_Roughness", roughness);
|
||||
m_SimplePBRShader->SetFloat("u_Metalness", 1.0f);
|
||||
m_SphereMesh->Render();
|
||||
|
||||
roughness += 0.15f;
|
||||
x += 22.0f;
|
||||
}
|
||||
|
||||
// Dielectrics
|
||||
roughness = 0.0f;
|
||||
x = -88.0f;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
m_SimplePBRShader->SetMat4("u_ModelMatrix", translate(mat4(1.0f), vec3(x, 22.0f, 0.0f)));
|
||||
m_SimplePBRShader->SetFloat("u_Roughness", roughness);
|
||||
m_SimplePBRShader->SetFloat("u_Metalness", 0.0f);
|
||||
m_SphereMesh->Render();
|
||||
|
||||
roughness += 0.15f;
|
||||
x += 22.0f;
|
||||
}
|
||||
|
||||
}
|
||||
else if (m_Scene == Scene::Model)
|
||||
{
|
||||
m_Mesh->Render();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
static bool show_demo_window = true;
|
||||
if (show_demo_window)
|
||||
ImGui::ShowDemoWindow(&show_demo_window);
|
||||
|
||||
ImGui::Begin("GameLayer");
|
||||
ImGui::ColorEdit4("Clear Color", m_ClearColor);
|
||||
ImGui::ColorEdit4("Triangle Color", glm::value_ptr(m_TriangleColor));
|
||||
ImGui::End();
|
||||
#define ENABLE_DOCKSPACE 1
|
||||
|
||||
#if ENABLE_DOCKSPACE
|
||||
|
||||
@ -189,10 +308,208 @@ void DemoLayer::OnImGuiRender()
|
||||
|
||||
ImGui::End();
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// Editor Panel ------------------------------------------------------------------------------
|
||||
ImGui::Begin("Settings");
|
||||
if (ImGui::TreeNode("Shaders"))
|
||||
{
|
||||
auto& shaders = Prism::Shader::s_AllShaders;
|
||||
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::RadioButton("Spheres", (int*)&m_Scene, (int)Scene::Spheres);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("Model", (int*)&m_Scene, (int)Scene::Model);
|
||||
|
||||
ImGui::ColorEdit4("Clear Color", m_ClearColor);
|
||||
|
||||
ImGui::SliderFloat3("Light Dir", glm::value_ptr(m_Light.Direction), -1, 1);
|
||||
ImGui::ColorEdit3("Light Radiance", glm::value_ptr(m_Light.Radiance));
|
||||
ImGui::SliderFloat("Light Multiplier", &m_LightMultiplier, 0.0f, 5.0f);
|
||||
ImGui::SliderFloat("Exposure", &m_Exposure, 0.0f, 10.0f);
|
||||
auto cameraForward = m_Camera.GetForwardDirection();
|
||||
ImGui::Text("Camera Forward: %.2f, %.2f, %.2f", cameraForward.x, cameraForward.y, cameraForward.z);
|
||||
|
||||
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();
|
||||
|
||||
ImGui::Text("Shader Parameters");
|
||||
ImGui::Checkbox("Radiance Prefiltering", &m_RadiancePrefilter);
|
||||
ImGui::SliderFloat("Env Map Rotation", &m_EnvMapRotation, -360.0f, 360.0f);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Textures ------------------------------------------------------------------------------
|
||||
{
|
||||
// Albedo
|
||||
if (ImGui::CollapsingHeader("Albedo", nullptr, ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10));
|
||||
ImGui::Image(m_AlbedoInput.TextureMap ? (void*)m_AlbedoInput.TextureMap->GetRendererID() : (void*)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((void*)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(m_NormalInput.TextureMap ? (void*)m_NormalInput.TextureMap->GetRendererID() : (void*)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((void*)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(m_MetalnessInput.TextureMap ? (void*)m_MetalnessInput.TextureMap->GetRendererID() : (void*)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((void*)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(m_RoughnessInput.TextureMap ? (void*)m_RoughnessInput.TextureMap->GetRendererID() : (void*)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((void*)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);
|
||||
}
|
||||
}
|
||||
|
||||
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((void*)m_FinalPresentBuffer->GetColorAttachmentRendererID(), viewportSize, { 0, 1 }, { 1, 0 });
|
||||
// ImGui::Image((void*)m_Framebuffer->GetColorAttachmentRendererID(), viewportSize, { 0, 1 }, { 1, 0 });
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -6,6 +6,9 @@
|
||||
#define DEMOLAYER_H
|
||||
|
||||
#include "Prism.h"
|
||||
#include "Prism/Renderer/Camera.h"
|
||||
#include "Prism/Renderer/FrameBuffer.h"
|
||||
#include "Prism/Renderer/Mesh.h"
|
||||
|
||||
class DemoLayer : public Prism::Layer
|
||||
{
|
||||
@ -20,12 +23,80 @@ public:
|
||||
virtual void OnEvent(Prism::Event& e) override;
|
||||
|
||||
private:
|
||||
float m_ClearColor[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
|
||||
glm::vec4 m_TriangleColor = { 0.4f, 0.5f, 0.6f, 1.0f };
|
||||
float m_ClearColor[4];
|
||||
|
||||
std::unique_ptr<Prism::Shader> m_Shader;
|
||||
std::unique_ptr<Prism::Shader> m_PBRShader;
|
||||
std::unique_ptr<Prism::Shader> m_SimplePBRShader;
|
||||
std::unique_ptr<Prism::Shader> m_QuadShader;
|
||||
std::unique_ptr<Prism::Shader> m_HDRShader;
|
||||
std::unique_ptr<Prism::Mesh> m_Mesh;
|
||||
std::unique_ptr<Prism::Mesh> m_SphereMesh;
|
||||
std::unique_ptr<Prism::Texture2D> m_BRDFLUT;
|
||||
|
||||
struct AlbedoInput
|
||||
{
|
||||
glm::vec3 Color = { 0.972f, 0.96f, 0.915f }; // Silver, from https://docs.unrealengine.com/en-us/Engine/Rendering/Materials/PhysicallyBased
|
||||
std::unique_ptr<Prism::Texture2D> TextureMap;
|
||||
bool SRGB = true;
|
||||
bool UseTexture = false;
|
||||
};
|
||||
AlbedoInput m_AlbedoInput;
|
||||
|
||||
struct NormalInput
|
||||
{
|
||||
std::unique_ptr<Prism::Texture2D> TextureMap;
|
||||
bool UseTexture = false;
|
||||
};
|
||||
NormalInput m_NormalInput;
|
||||
|
||||
struct MetalnessInput
|
||||
{
|
||||
float Value = 1.0f;
|
||||
std::unique_ptr<Prism::Texture2D> TextureMap;
|
||||
bool UseTexture = false;
|
||||
};
|
||||
MetalnessInput m_MetalnessInput;
|
||||
|
||||
struct RoughnessInput
|
||||
{
|
||||
float Value = 0.5f;
|
||||
std::unique_ptr<Prism::Texture2D> TextureMap;
|
||||
bool UseTexture = false;
|
||||
};
|
||||
RoughnessInput m_RoughnessInput;
|
||||
|
||||
std::unique_ptr<Prism::FrameBuffer> m_Framebuffer, m_FinalPresentBuffer;
|
||||
|
||||
std::unique_ptr<Prism::VertexBuffer> m_VertexBuffer;
|
||||
std::unique_ptr<Prism::IndexBuffer> m_IndexBuffer;
|
||||
std::unique_ptr<Prism::Shader> m_Shader;
|
||||
std::unique_ptr<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
|
||||
std::unique_ptr<Prism::Texture2D> m_CheckerboardTex;
|
||||
};
|
||||
|
||||
|
||||
|
||||
256
Sandbox/Sandbox/Layer/TestLayer.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
//
|
||||
// Created by sfd on 25-11-23.
|
||||
//
|
||||
|
||||
#include "TestLayer.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "glm/gtc/type_ptr.hpp"
|
||||
#include "Prism/Renderer/Renderer.h"
|
||||
#include "Prism/Renderer/Shader.h"
|
||||
|
||||
|
||||
static void EnableDockSpace(const bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
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();
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TestLayer::TestLayer()
|
||||
:m_Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f))
|
||||
{
|
||||
}
|
||||
|
||||
void TestLayer::OnAttach()
|
||||
{
|
||||
m_FrameBuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA16F));
|
||||
m_FinalPresentBuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA8));
|
||||
|
||||
static float QuadVertex[] = {
|
||||
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
|
||||
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
|
||||
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
|
||||
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
static unsigned int QuadIndices[] = {
|
||||
0, 1, 2, 2, 3, 0
|
||||
};
|
||||
|
||||
m_VertexBuffer = std::unique_ptr<Prism::VertexBuffer>(Prism::VertexBuffer::Create());
|
||||
m_VertexBuffer->SetData(QuadVertex, sizeof(QuadVertex));
|
||||
|
||||
m_IndexBuffer = std::unique_ptr<Prism::IndexBuffer>(Prism::IndexBuffer::Create());
|
||||
m_IndexBuffer->SetData(QuadIndices, sizeof(QuadIndices));
|
||||
|
||||
m_SkyBoxTextureCube.reset(Prism::TextureCube::Create("assets/textures/environments/Arches_E_PineTree_Radiance.tga"));
|
||||
|
||||
m_SkyboxShader.reset(Prism::Shader::Create("assets/shaders/quad.glsl"));
|
||||
m_Shader.reset(Prism::Shader::Create("assets/shaders/demo.glsl"));
|
||||
m_HDRShader.reset(Prism::Shader::Create("assets/shaders/hdr.glsl"));
|
||||
|
||||
m_Mesh.reset(new Prism::Mesh("assets/meshes/cerberus.fbx"));
|
||||
}
|
||||
|
||||
void TestLayer::OnDetach()
|
||||
{
|
||||
}
|
||||
|
||||
void TestLayer::OnUpdate()
|
||||
{
|
||||
|
||||
m_Camera.Update();
|
||||
auto viewProjection = m_Camera.GetProjectionMatrix() * m_Camera.GetViewMatrix();
|
||||
|
||||
m_FrameBuffer->Bind();
|
||||
Prism::Renderer::Clear(m_clearColor[0], m_clearColor[1], m_clearColor[2], m_clearColor[3]);
|
||||
|
||||
Prism::UniformBufferDeclaration<sizeof(glm::mat4), 1> skyboxShaderUB;
|
||||
skyboxShaderUB.Push("u_InverseVP", inverse(viewProjection));
|
||||
m_SkyboxShader->UploadUniformBuffer(skyboxShaderUB);
|
||||
m_SkyboxShader->Bind();
|
||||
m_SkyBoxTextureCube->Bind(0);
|
||||
m_VertexBuffer->Bind();
|
||||
m_IndexBuffer->Bind();
|
||||
Prism::Renderer::DrawIndexed(m_IndexBuffer->GetCount(), false);
|
||||
|
||||
Prism::UniformBufferDeclaration<sizeof(glm::vec4) + sizeof(glm::mat4), 2> uniformbuffer;
|
||||
uniformbuffer.Push("u_Color", m_TriangleColor);
|
||||
uniformbuffer.Push("u_MVP", viewProjection);
|
||||
m_Shader->UploadUniformBuffer(uniformbuffer);
|
||||
|
||||
m_Shader->Bind();
|
||||
|
||||
m_Mesh->Render();
|
||||
// m_VertexBuffer->Bind();
|
||||
// m_IndexBuffer->Bind();
|
||||
|
||||
// m_Mesh->Render();
|
||||
// Prism::Renderer::DrawIndexed(3, false);
|
||||
m_FrameBuffer->Unbind();
|
||||
|
||||
// HDR
|
||||
m_FinalPresentBuffer->Bind();
|
||||
m_HDRShader->Bind();
|
||||
m_HDRShader->SetFloat("u_Exposure", m_Exposure);
|
||||
m_FrameBuffer->BindTexture();
|
||||
m_VertexBuffer->Bind();
|
||||
m_IndexBuffer->Bind();
|
||||
Prism::Renderer::DrawIndexed(m_IndexBuffer->GetCount(), false);
|
||||
m_FinalPresentBuffer->Unbind();
|
||||
}
|
||||
|
||||
void TestLayer::OnImGuiRender()
|
||||
{
|
||||
EnableDockSpace(true);
|
||||
|
||||
ImGui::Begin("Settings");
|
||||
ImGui::ColorEdit4("ClearColor", glm::value_ptr(m_clearColor));
|
||||
ImGui::ColorEdit4("TriangleClearColor", glm::value_ptr(m_TriangleColor));
|
||||
ImGui::DragFloat("Exposure", &m_Exposure, 0.01f, 0.0f);
|
||||
const auto& position = m_Camera.GetPosition();
|
||||
ImGui::Text("Camera: (%.2f, %.2f, %.2f)", position.x, position.y, position.z);
|
||||
ImGui::End();
|
||||
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
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((void*)m_FinalPresentBuffer->GetColorAttachmentRendererID(), viewportSize, {0, 1}, {1, 0});
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void TestLayer::OnEvent(Prism::Event& e)
|
||||
{
|
||||
}
|
||||
49
Sandbox/Sandbox/Layer/TestLayer.h
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// Created by sfd on 25-11-23.
|
||||
//
|
||||
|
||||
#ifndef TESTLAYER_H
|
||||
#define TESTLAYER_H
|
||||
#include "glm/vec4.hpp"
|
||||
#include "Prism/Core/Layer.h"
|
||||
#include "Prism/Renderer/Buffer.h"
|
||||
#include "Prism/Renderer/Camera.h"
|
||||
#include "Prism/Renderer/FrameBuffer.h"
|
||||
#include "Prism/Renderer/Mesh.h"
|
||||
#include "Prism/Renderer/Shader.h"
|
||||
#include "Prism/Renderer/Texture.h"
|
||||
|
||||
|
||||
class TestLayer : public Prism::Layer
|
||||
{
|
||||
public:
|
||||
TestLayer();
|
||||
void OnAttach() override;
|
||||
void OnDetach() override;
|
||||
void OnUpdate() override;
|
||||
void OnImGuiRender() override;
|
||||
void OnEvent(Prism::Event& e) override;
|
||||
|
||||
private:
|
||||
glm::vec4 m_clearColor = glm::vec4(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glm::vec4 m_TriangleColor = glm::vec4(1.0f);
|
||||
|
||||
std::unique_ptr<Prism::FrameBuffer> m_FrameBuffer, m_FinalPresentBuffer;
|
||||
std::unique_ptr<Prism::VertexBuffer> m_VertexBuffer;
|
||||
std::unique_ptr<Prism::IndexBuffer> m_IndexBuffer;
|
||||
|
||||
|
||||
std::unique_ptr<Prism::TextureCube> m_SkyBoxTextureCube;
|
||||
|
||||
std::unique_ptr<Prism::Shader> m_SkyboxShader;
|
||||
std::unique_ptr<Prism::Shader> m_Shader;
|
||||
std::unique_ptr<Prism::Shader> m_HDRShader;
|
||||
std::unique_ptr<Prism::Mesh> m_Mesh;
|
||||
|
||||
Prism::Camera m_Camera;
|
||||
|
||||
float m_Exposure = 1.0f;
|
||||
};
|
||||
|
||||
|
||||
#endif //TESTLAYER_H
|
||||
@ -7,6 +7,7 @@
|
||||
#include "Prism/Core/EntryPoint.h"
|
||||
|
||||
#include "Layer/DemoLayer.h"
|
||||
#include "Layer/TestLayer.h"
|
||||
|
||||
class Sandbox : public Prism::Application
|
||||
{
|
||||
@ -17,7 +18,8 @@ public:
|
||||
|
||||
virtual void OnInit() override
|
||||
{
|
||||
PushLayer(new DemoLayer());
|
||||
PushLayer(new TestLayer());
|
||||
// PushLayer(new DemoLayer());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
BIN
Sandbox/assets/editor/Checkerboard.tga
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Sandbox/assets/meshes/Anya.fbx
Normal file
BIN
Sandbox/assets/meshes/cerberus.fbx
Normal file
23
Sandbox/assets/meshes/skybox.obj
Normal file
@ -0,0 +1,23 @@
|
||||
# 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
|
||||
BIN
Sandbox/assets/models/Cube.fbx
Normal file
BIN
Sandbox/assets/models/Sphere.fbx
Normal file
BIN
Sandbox/assets/models/SphereOld.fbx
Normal file
29
Sandbox/assets/shaders/demo.glsl
Normal file
@ -0,0 +1,29 @@
|
||||
#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);
|
||||
}
|
||||
43
Sandbox/assets/shaders/hdr.glsl
Normal file
@ -0,0 +1,43 @@
|
||||
#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);
|
||||
}
|
||||
33
Sandbox/assets/shaders/quad.glsl
Normal file
@ -0,0 +1,33 @@
|
||||
// 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);
|
||||
}
|
||||
@ -1,21 +1,32 @@
|
||||
#type vertex
|
||||
#version 430 core
|
||||
#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 = vec4(a_Position, 1.0);
|
||||
gl_Position = u_MVP * vec4(a_Position, 1.0);
|
||||
v_Normal = a_Normal;
|
||||
}
|
||||
|
||||
#type fragment
|
||||
#version 430 core
|
||||
#version 430
|
||||
|
||||
layout(location = 0) out vec4 o_Color;
|
||||
layout(location = 0) out vec4 finalColor;
|
||||
|
||||
uniform vec4 u_Color;
|
||||
//uniform vec4 u_Color;
|
||||
|
||||
in vec3 v_Normal;
|
||||
|
||||
void main()
|
||||
{
|
||||
o_Color = u_Color;
|
||||
}
|
||||
finalColor = vec4((v_Normal * 0.5 + 0.5), 1.0);// * u_Color.xyz, 1.0);
|
||||
}
|
||||
319
Sandbox/assets/shaders/simplepbr.glsl
Normal file
@ -0,0 +1,319 @@
|
||||
// -----------------------------
|
||||
// -- 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;
|
||||
} vs_Output;
|
||||
|
||||
void main()
|
||||
{
|
||||
vs_Output.WorldPosition = vec3(mat4(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);
|
||||
|
||||
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;
|
||||
} 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);
|
||||
}
|
||||
BIN
Sandbox/assets/textures/BRDF_LUT.tga
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
Sandbox/assets/textures/SphereAlbedo.tga
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
Sandbox/assets/textures/SphereMetalness.tga
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
Sandbox/assets/textures/SphereRoughness.tga
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
Sandbox/assets/textures/cerberus/cerberus_A.png
Normal file
|
After Width: | Height: | Size: 15 MiB |
BIN
Sandbox/assets/textures/cerberus/cerberus_M.png
Normal file
|
After Width: | Height: | Size: 6.5 MiB |
BIN
Sandbox/assets/textures/cerberus/cerberus_N.png
Normal file
|
After Width: | Height: | Size: 19 MiB |
BIN
Sandbox/assets/textures/cerberus/cerberus_R.png
Normal file
|
After Width: | Height: | Size: 11 MiB |
|
After Width: | Height: | Size: 576 KiB |
|
After Width: | Height: | Size: 22 MiB |