add static and animation PBR shader, improve [camera renderer material] class

This commit is contained in:
2025-11-28 23:32:07 +08:00
parent 75965135af
commit 4c7b79ec8a
29 changed files with 982 additions and 394 deletions

View File

@ -36,6 +36,7 @@ namespace Prism
PushOverlay(m_ImGuiLayer);
Renderer::Init();
Renderer::Get().WaitAndRender();
}
Application::~Application()

View File

@ -63,6 +63,12 @@ namespace Prism
template<typename T>
using Ref = std::shared_ptr<T>;
template<typename T, typename ... Args>
constexpr Ref<T> CreateRef(Args&& ... args)
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
}
#endif //CORE_H

View File

@ -38,6 +38,7 @@ namespace Prism
virtual void SetEventCallback(const EventCallbackFn& callback) = 0;
virtual unsigned int GetWidth() const = 0;
virtual unsigned int GetHeight() const = 0;
virtual std::pair<float, float> GetWindowPos() const = 0;
virtual void SetVSync(bool enable) = 0;
virtual bool const IsVSync() const = 0;

View File

@ -11,9 +11,11 @@
namespace Prism
{
OpenGLFrameBuffer::OpenGLFrameBuffer(const uint32_t width, const uint32_t height, const FramebufferFormat format)
: m_Width(width), m_Height(height), m_Format(format)
: m_Format(format)
{
Resize(width, height);
OpenGLFrameBuffer::Resize(width, height);
m_Width = width;
m_Height = height;
}
OpenGLFrameBuffer::~OpenGLFrameBuffer()

View File

@ -31,7 +31,7 @@ namespace Prism
virtual FramebufferFormat GetFormat() const { return m_Format; }
private:
RendererID m_RendererID = 0;
uint32_t m_Width, m_Height;
uint32_t m_Width = 0, m_Height = 0;
FramebufferFormat m_Format;
RendererID m_ColorAttachment, m_DepthAttachment;

View File

@ -27,6 +27,7 @@ namespace Prism
void RendererAPI::Init()
{
glDebugMessageCallback(OpenGLLogMessage, nullptr);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
unsigned int vao;

View File

@ -12,8 +12,12 @@ namespace Prism
OpenGLShader::OpenGLShader(const std::string& filepath)
: m_AssetPath(filepath)
{
const size_t found = filepath.find_last_of("/\\");
size_t found = filepath.find_last_of("/\\");
m_Name = found != std::string::npos ? filepath.substr(found + 1) : filepath;
found = m_Name.find_last_of('.');
m_Name = found != std::string::npos ? m_Name.substr(0, found) : m_Name;
OpenGLShader::Reload();
}

View File

@ -83,7 +83,7 @@ namespace Prism
inline const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const override { return *m_PSMaterialUniformBuffer; }
inline const ShaderResourceList& GetResources() const override { return m_Resources; }
private:
RendererID m_RendererID;
RendererID m_RendererID = 0;
bool m_Loaded = false;

View File

@ -58,6 +58,13 @@ namespace Prism
m_Data.VSync = enable;
}
std::pair<float, float> WindowsWindow::GetWindowPos() const
{
int x, y;
glfwGetWindowPos(m_Window, &x, &y);
return { x, y };
}
void WindowsWindow::Init(const WindowProps& props)
{
m_Data.Title = props.Title;

View File

@ -24,6 +24,7 @@ namespace Prism
inline void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; }
bool const IsVSync() const override { return m_Data.VSync; }
void SetVSync(bool enable) override;
virtual std::pair<float, float> GetWindowPos() const override;
inline void* GetNativeWindow() const { return m_Window; }

View File

@ -14,7 +14,7 @@ namespace Prism
public:
virtual ~VertexBuffer() {}
virtual void SetData(void* buffer, unsigned int size, unsigned int offset = 0) = 0;
virtual void SetData(void* buffer, uint32_t size, uint32_t offset = 0) = 0;
virtual void Bind() const = 0;
virtual unsigned int GetSize() const = 0;

View File

@ -22,11 +22,6 @@ namespace Prism
Camera::Camera(const glm::mat4& projectionMatrix)
: m_ProjectionMatrix(projectionMatrix)
{
// Sensible defaults
m_PanSpeed = 0.15f;
m_RotationSpeed = 0.5f;
m_ZoomSpeed = 10.0f;
m_Position = { 0, 0, 10 };
m_Rotation = glm::vec3(90.0f, 0.0f, 0.0f);
@ -85,23 +80,25 @@ namespace Prism
void Camera::MousePan(const glm::vec2& delta)
{
m_FocalPoint += -GetRightDirection() * delta.x * m_PanSpeed * m_Distance;
m_FocalPoint += GetUpDirection() * delta.y * m_PanSpeed * m_Distance;
auto [xSpeed, ySpeed] = PanSpeed();
// PM_CORE_TRACE("{0}, {1}", xSpeed, ySpeed);
m_FocalPoint += -GetRightDirection() * delta.x * xSpeed * m_Distance;
m_FocalPoint += GetUpDirection() * delta.y * ySpeed * m_Distance;
}
void Camera::MouseRotate(const glm::vec2& delta)
{
float yawSign = GetUpDirection().y < 0 ? -1.0f : 1.0f;
m_Yaw += yawSign * delta.x * m_RotationSpeed;
m_Pitch += delta.y * m_RotationSpeed;
m_Yaw += yawSign * delta.x * RotationSpeed();
m_Pitch += delta.y * RotationSpeed();
}
void Camera::MouseZoom(float delta)
void Camera::MouseZoom(const float delta)
{
m_Distance -= delta * m_ZoomSpeed;
m_Distance -= delta * ZoomSpeed();
if (m_Distance < 1.0f)
{
m_FocalPoint += GetForwardDirection();
m_FocalPoint += GetForwardDirection() * delta;
m_Distance = 1.0f;
}
}
@ -115,4 +112,29 @@ namespace Prism
{
return glm::quat(glm::vec3(-m_Pitch, -m_Yaw, 0.0f));
}
std::pair<float, float> Camera::PanSpeed() const
{
const float x = std::min(m_ViewportWidth / 1000.0f, 2.4f); // max = 2.4f
float xFactor = 0.0366f * (x * x) - 0.1778f * x + 0.3021f;
const float y = std::min(m_ViewportHeight / 1000.0f, 2.4f); // max = 2.4f
float yFactor = 0.0366f * (y * y) - 0.1778f * y + 0.3021f;
return { xFactor, yFactor };
}
float Camera::RotationSpeed() const
{
return 0.8f;
}
float Camera::ZoomSpeed() const
{
float distance = m_Distance * 0.2f;
distance = std::max(distance, 0.0f);
float speed = distance * distance;
speed = std::min(speed, 100.0f); // max speed = 100
return speed;
}
}

View File

@ -32,6 +32,8 @@ namespace Prism
glm::vec3 GetRightDirection();
glm::vec3 GetForwardDirection();
const glm::vec3& GetPosition() const { return m_Position; }
public:
inline void SetViewportSize(const uint32_t width, const uint32_t height) { m_ViewportWidth = width; m_ViewportHeight = height; }
private:
void MousePan(const glm::vec2& delta);
void MouseRotate(const glm::vec2& delta);
@ -39,6 +41,10 @@ namespace Prism
glm::vec3 CalculatePosition();
glm::quat GetOrientation();
std::pair<float, float> PanSpeed() const;
float RotationSpeed() const;
float ZoomSpeed() const;
private:
glm::mat4 m_ProjectionMatrix, m_ViewMatrix;
glm::vec3 m_Position, m_Rotation, m_FocalPoint;
@ -47,8 +53,9 @@ namespace Prism
glm::vec2 m_InitialMousePosition;
glm::vec3 m_InitialFocalPoint, m_InitialRotation;
uint32_t m_ViewportWidth = 1280, m_ViewportHeight = 720;
float m_Distance;
float m_PanSpeed, m_RotationSpeed, m_ZoomSpeed;
float m_Pitch, m_Yaw;
};

View File

@ -17,6 +17,10 @@ namespace Prism
{
}
Ref<Material> Material::Create(const Ref<Shader>& shader)
{
return CreateRef<Material>(shader);
}
void Material::AllocateStorage()
@ -120,6 +124,11 @@ namespace Prism
m_Material->m_MaterialInstances.erase(this);
}
Ref<MaterialInstance> MaterialInstance::Create(const Ref<Material>& material)
{
return CreateRef<MaterialInstance>(material);
}
void MaterialInstance::OnShaderReloaded()
{
AllocateStorage();

View File

@ -23,6 +23,8 @@ namespace Prism
Material(const Ref<Shader>& shader);
virtual ~Material();
static Ref<Material> Create(const Ref<Shader>& shader);
void Bind() const;
template <typename T>
@ -31,7 +33,8 @@ namespace Prism
void Set(const std::string& name, const Ref<Texture>& texture)
{
auto decl = FindResourceDeclaration(name);
const auto decl = FindResourceDeclaration(name);
if (!decl) return;
uint32_t slot = decl->GetRegister();
if (m_Textures.size() <= slot)
m_Textures.resize((size_t)slot + 1);
@ -55,6 +58,7 @@ namespace Prism
ShaderUniformDeclaration* FindUniformDeclaration(const std::string& name);
ShaderResourceDeclaration* FindResourceDeclaration(const std::string& name);
Buffer& GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration);
private:
Ref<Shader> m_Shader;
std::unordered_set<MaterialInstance*> m_MaterialInstances;
@ -75,6 +79,8 @@ namespace Prism
MaterialInstance(const Ref<Material>& material);
virtual ~MaterialInstance();
static Ref<MaterialInstance> Create(const Ref<Material>& material);
template <typename T>
void Set(const std::string& name, const T& value)
{

View File

@ -57,7 +57,7 @@ namespace Prism
}
void Vertex::AddBoneData(uint32_t BoneID, float Weight)
void AnimatedVertex::AddBoneData(uint32_t BoneID, float Weight)
{
for (size_t i = 0; i < 4; i++)
{
@ -103,6 +103,9 @@ namespace Prism
PM_CORE_ERROR("Failed to load mesh file: {0}", filename);
m_IsAnimated = scene->mAnimations != nullptr;
m_MeshShader = m_IsAnimated ? Renderer::GetShaderLibrary()->Get("simplepbr_Anim") : Renderer::GetShaderLibrary()->Get("simplepbr_Static");
m_Material.reset(new Prism::Material(m_MeshShader));
m_InverseTransform = glm::inverse(aiMatrix4x4ToGlm(scene->mRootNode->mTransformation));
uint32_t vertexCount = 0;
@ -127,6 +130,26 @@ namespace Prism
PM_CORE_ASSERT(mesh->HasNormals(), "Meshes require normals.");
// Vertices
if (m_IsAnimated)
{
for (size_t i = 0; i < mesh->mNumVertices; i++)
{
AnimatedVertex vertex;
vertex.Position = { mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z };
vertex.Normal = { mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z };
if (mesh->HasTangentsAndBitangents())
{
vertex.Tangent = { mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z };
vertex.Binormal = { mesh->mBitangents[i].x, mesh->mBitangents[i].y, mesh->mBitangents[i].z };
}
if (mesh->HasTextureCoords(0))
vertex.Texcoord = { mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y };
m_AnimatedVertices.push_back(vertex);
}
}else
{
for (size_t i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
@ -141,7 +164,9 @@ namespace Prism
if (mesh->HasTextureCoords(0))
vertex.Texcoord = { mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y };
m_Vertices.push_back(vertex);
m_StaticVertices.push_back(vertex);
}
}
// Indices
@ -152,7 +177,15 @@ namespace Prism
}
}
PM_CORE_TRACE("NODES:");
PM_CORE_TRACE("-----------------------------");
TraverseNodes(scene->mRootNode);
PM_CORE_TRACE("-----------------------------");
// Bones
if (m_IsAnimated)
{
for (size_t m = 0; m < scene->mNumMeshes; m++)
{
aiMesh* mesh = scene->mMeshes[m];
@ -184,13 +217,17 @@ namespace Prism
{
int VertexID = submesh.BaseVertex + bone->mWeights[j].mVertexId;
float Weight = bone->mWeights[j].mWeight;
m_Vertices[VertexID].AddBoneData(boneIndex, Weight);
m_AnimatedVertices[VertexID].AddBoneData(boneIndex, Weight);
}
}
}
}
m_VertexBuffer.reset(VertexBuffer::Create());
m_VertexBuffer->SetData(m_Vertices.data(), m_Vertices.size() * sizeof(Vertex));
if (m_IsAnimated)
m_VertexBuffer->SetData(m_AnimatedVertices.data(), m_AnimatedVertices.size() * sizeof(AnimatedVertex));
else
m_VertexBuffer->SetData(m_StaticVertices.data(), m_StaticVertices.size() * sizeof(Vertex));
m_IndexBuffer.reset(IndexBuffer::Create());
m_IndexBuffer->SetData(m_Indices.data(), m_Indices.size() * sizeof(Index));
@ -202,9 +239,16 @@ namespace Prism
{
}
void Mesh::Render(TimeStep deltaTime, Shader* shader)
void Mesh::Render(TimeStep deltaTime, const Ref<MaterialInstance>& materialInstance)
{
if (m_AnimationPlaying && m_Scene->mAnimations)
Render(deltaTime, glm::mat4(1.0f), materialInstance);
}
void Mesh::Render(TimeStep deltaTime, const glm::mat4& transform, const Ref<MaterialInstance>& materialInstance)
{
if (m_IsAnimated)
{
if (m_AnimationPlaying)
{
m_WorldTime += deltaTime;
@ -213,16 +257,55 @@ namespace Prism
m_AnimationTime = fmod(m_AnimationTime, (float)m_Scene->mAnimations[0]->mDuration);
}
if (m_Scene->mAnimations)
BoneTransform(m_AnimationTime);
}
if (materialInstance)
materialInstance->Bind();
// TODO: Sort this out
m_VertexBuffer->Bind();
m_IndexBuffer->Bind();
bool materialOverride = !!materialInstance;
// TODO: replace with render API calls
PM_RENDER_S1(shader, {
for (Submesh& submesh : self->m_Submeshes)
PM_RENDER_S2(transform, materialOverride,{
for (const Submesh& submesh : self->m_Submeshes)
{
if (self->m_IsAnimated)
{
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(AnimatedVertex), (const void*)offsetof(AnimatedVertex, Position));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(AnimatedVertex), (const void*)offsetof(AnimatedVertex, Normal));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(AnimatedVertex), (const void*)offsetof(AnimatedVertex, Tangent));
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(AnimatedVertex), (const void*)offsetof(AnimatedVertex, Binormal));
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(AnimatedVertex), (const void*)offsetof(AnimatedVertex, Texcoord));
glEnableVertexAttribArray(5);
glVertexAttribIPointer(5, 4, GL_INT, sizeof(AnimatedVertex), (const void*)offsetof(AnimatedVertex, IDs));
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(AnimatedVertex), (const void*)offsetof(AnimatedVertex, Weights));
if (self->m_Scene->mAnimations)
{
for (size_t i = 0; i < self->m_BoneTransforms.size(); i++)
{
std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]");
self->m_MeshShader->SetMat4FromRenderThread(uniformName, self->m_BoneTransforms[i]);
}
}
glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex);
}else
{
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Position));
@ -238,21 +321,9 @@ namespace Prism
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Texcoord));
glEnableVertexAttribArray(5);
glVertexAttribIPointer(5, 4, GL_INT, sizeof(Vertex), (const void*)offsetof(Vertex, IDs));
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Weights));
if (self->m_Scene->mAnimations)
{
for (size_t i = 0; i < self->m_BoneTransforms.size(); i++)
{
std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]");
shader->SetMat4FromRenderThread(uniformName, self->m_BoneTransforms[i]);
}
}
if (!materialOverride)
self->m_MeshShader->SetMat4FromRenderThread("u_ModelMatrix", transform * submesh.Transform);
glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex);
}
});
@ -262,6 +333,8 @@ namespace Prism
{
ImGui::Begin("Mesh Debug");
if (ImGui::CollapsingHeader(m_FilePath.c_str()))
{
if (m_IsAnimated)
{
if (ImGui::CollapsingHeader("Animation"))
{
@ -272,6 +345,7 @@ namespace Prism
ImGui::DragFloat("Time Scale", &m_TimeMultiplier, 0.05f, 0.0f, 10.0f);
}
}
}
ImGui::End();
}
@ -282,9 +356,11 @@ namespace Prism
PM_CORE_TRACE("------------------------------------------------------");
PM_CORE_TRACE("Vertex Buffer Dump");
PM_CORE_TRACE("Mesh: {0}", m_FilePath);
for (size_t i = 0; i < m_Vertices.size(); i++)
if (m_IsAnimated)
{
auto& vertex = m_Vertices[i];
for (size_t i = 0; i < m_AnimatedVertices.size(); i++)
{
auto& vertex = m_AnimatedVertices[i];
PM_CORE_TRACE("Vertex: {0}", i);
PM_CORE_TRACE("Position: {0}, {1}, {2}", vertex.Position.x, vertex.Position.y, vertex.Position.z);
PM_CORE_TRACE("Normal: {0}, {1}, {2}", vertex.Normal.x, vertex.Normal.y, vertex.Normal.z);
@ -293,6 +369,20 @@ namespace Prism
PM_CORE_TRACE("TexCoord: {0}, {1}", vertex.Texcoord.x, vertex.Texcoord.y);
PM_CORE_TRACE("--");
}
}else
{
for (size_t i = 0; i < m_StaticVertices.size(); i++)
{
auto& vertex = m_StaticVertices[i];
PM_CORE_TRACE("Vertex: {0}", i);
PM_CORE_TRACE("Position: {0}, {1}, {2}", vertex.Position.x, vertex.Position.y, vertex.Position.z);
PM_CORE_TRACE("Normal: {0}, {1}, {2}", vertex.Normal.x, vertex.Normal.y, vertex.Normal.z);
PM_CORE_TRACE("Binormal: {0}, {1}, {2}", vertex.Binormal.x, vertex.Binormal.y, vertex.Binormal.z);
PM_CORE_TRACE("Tangent: {0}, {1}, {2}", vertex.Tangent.x, vertex.Tangent.y, vertex.Tangent.z);
PM_CORE_TRACE("TexCoord: {0}, {1}", vertex.Texcoord.x, vertex.Texcoord.y);
PM_CORE_TRACE("--");
}
}
PM_CORE_TRACE("------------------------------------------------------");
}
@ -337,6 +427,25 @@ namespace Prism
ReadNodeHierarchy(AnimationTime, pNode->mChildren[i], transform);
}
void Mesh::TraverseNodes(aiNode* node, int level)
{
std::string levelText;
for (int i = 0; i < level; i++)
levelText += "-";
PM_CORE_TRACE("{0}Node name: {1}", levelText, std::string(node->mName.data));
for (uint32_t i = 0; i < node->mNumMeshes; i++)
{
uint32_t mesh = node->mMeshes[i];
m_Submeshes[mesh].Transform = aiMatrix4x4ToGlm(node->mTransformation);
}
for (uint32_t i = 0; i < node->mNumChildren; i++)
{
aiNode* child = node->mChildren[i];
TraverseNodes(child, level + 1);
}
}
const aiNodeAnim* Mesh::FindNodeAnim(const aiAnimation* animation, const std::string& nodeName)
{
for (uint32_t i = 0; i < animation->mNumChannels; i++)

View File

@ -8,6 +8,7 @@
#include <glm/glm.hpp>
#include "Buffer.h"
#include "Material.h"
#include "Shader.h"
#include "Prism/Core/TimeStep.h"
@ -24,6 +25,15 @@ namespace Assimp
namespace Prism
{
struct Vertex
{
glm::vec3 Position;
glm::vec3 Normal;
glm::vec3 Tangent;
glm::vec3 Binormal;
glm::vec2 Texcoord;
};
struct AnimatedVertex
{
glm::vec3 Position;
glm::vec3 Normal;
@ -72,6 +82,8 @@ namespace Prism
uint32_t BaseIndex;
uint32_t MaterialIndex;
uint32_t IndexCount;
glm::mat4 Transform;
};
class PRISM_API Mesh
@ -82,14 +94,18 @@ namespace Prism
Mesh(const std::string& filename);
~Mesh();
void Render(TimeStep deltaTime, Shader* shader);
void Render(TimeStep deltaTime, const Ref<MaterialInstance>& materialInstance = Ref<MaterialInstance>());
void Render(TimeStep deltaTime, const glm::mat4& transform = glm::mat4(1.0f), const Ref<MaterialInstance>& materialInstance = Ref<MaterialInstance>());
void OnImGuiRender();
void DumpVertexBuffer();
inline Ref<Shader> GetMeshShader() { return m_MeshShader; }
inline Ref<Material> GetMaterial() { return m_Material; }
inline const std::string& GetFilePath() const { return m_FilePath; }
private:
void BoneTransform(float time);
void ReadNodeHierarchy(float AnimationTime, const aiNode* pNode, const glm::mat4& ParentTransform);
void TraverseNodes(aiNode* node, int level = 0);
const aiNodeAnim* FindNodeAnim(const aiAnimation* animation, const std::string& nodeName);
uint32_t FindPosition(float AnimationTime, const aiNodeAnim* pNodeAnim);
@ -112,13 +128,20 @@ namespace Prism
std::unique_ptr<VertexBuffer> m_VertexBuffer;
std::unique_ptr<IndexBuffer> m_IndexBuffer;
std::vector<Vertex> m_Vertices;
std::vector<Vertex> m_StaticVertices;
std::vector<AnimatedVertex> m_AnimatedVertices;
std::vector<Index> m_Indices;
std::unordered_map<std::string, uint32_t> m_BoneMapping;
std::vector<glm::mat4> m_BoneTransforms;
const aiScene* m_Scene;
// Material
Ref<Shader> m_MeshShader;
Ref<Material> m_Material;
// Animation
bool m_IsAnimated = false;
float m_AnimationTime = 0.0f;
float m_WorldTime = 0.0f;
float m_TimeMultiplier = 1.0f;

View File

@ -41,7 +41,11 @@ namespace Prism
void Renderer::Init()
{
s_Instance->m_ShaderLibrary = std::make_unique<ShaderLibrary>();
PM_RENDER({ RendererAPI::Init(); });
GetShaderLibrary()->Load("assets/shaders/simplepbr_Static.glsl");
GetShaderLibrary()->Load("assets/shaders/simplepbr_Anim.glsl");
}
void Renderer::WaitAndRender()

View File

@ -5,6 +5,7 @@
#ifndef RENDERER_H
#define RENDERER_H
#include "RenderCommandQueue.h"
#include "Shader.h"
#include "Prism/Core/Application.h"
@ -25,6 +26,8 @@ namespace Prism
static void Init();
static const Scope<ShaderLibrary>& GetShaderLibrary() { return Get().m_ShaderLibrary; }
static void* Submit(const RenderCommandFn func, const unsigned int size)
{
return s_Instance->m_CommandQueue.Allocate(func, size);
@ -35,6 +38,7 @@ namespace Prism
private:
static Renderer* s_Instance;
Scope<ShaderLibrary> m_ShaderLibrary;
RenderCommandQueue m_CommandQueue;
};

View File

@ -9,16 +9,16 @@
namespace Prism
{
std::vector<Shader*> Shader::s_AllShaders;
std::vector<Ref<Shader>> Shader::s_AllShaders;
Shader* Shader::Create(const std::string& filepath)
Ref<Shader> Shader::Create(const std::string& filepath)
{
Shader* result = nullptr;
Ref<Shader> result = nullptr;
switch (RendererAPI::Current())
{
case RendererAPIType::None: return nullptr;
case RendererAPIType::OpenGL: result = new OpenGLShader(filepath);
case RendererAPIType::OpenGL: result = CreateRef<OpenGLShader>(filepath);
}
s_AllShaders.push_back(result);
@ -26,8 +26,43 @@ namespace Prism
return result;
}
std::vector<Shader*>& Shader::GetAllShaders()
std::vector<Ref<Shader>>& Shader::GetAllShaders()
{
return s_AllShaders;
}
ShaderLibrary::ShaderLibrary()
{
}
ShaderLibrary::~ShaderLibrary()
{
}
void ShaderLibrary::Add(const Ref<Shader>& shader)
{
auto& name = shader->GetName();
PM_CORE_ASSERT(m_Shaders.find(name) == m_Shaders.end());
m_Shaders[name] = shader;
}
void ShaderLibrary::Load(const std::string& path)
{
auto shader = Ref<Shader>(Shader::Create(path));
auto& name = shader->GetName();
PM_CORE_ASSERT(m_Shaders.find(name) == m_Shaders.end());
m_Shaders[name] = shader;
}
void ShaderLibrary::Load(const std::string& name, const std::string& path)
{
PM_CORE_ASSERT(m_Shaders.find(name) == m_Shaders.end());
m_Shaders[name] = Ref<Shader>(Shader::Create(path));
}
Ref<Shader>& ShaderLibrary::Get(const std::string& name)
{
PM_CORE_ASSERT(m_Shaders.find(name) != m_Shaders.end());
return m_Shaders[name];
}
}

View File

@ -122,11 +122,26 @@ namespace Prism
virtual void AddShaderReloadedCallback(const ShaderReloadedCallback& callback) = 0;
static Shader* Create(const std::string& filepath);
static std::vector<Shader*>& GetAllShaders();
static Ref<Shader> Create(const std::string& filepath);
static std::vector<Ref<Shader>>& GetAllShaders();
private:
static std::vector<Shader*> s_AllShaders;
static std::vector<Ref<Shader>> s_AllShaders;
};
class ShaderLibrary
{
public:
ShaderLibrary();
~ShaderLibrary();
void Add(const Ref<Shader>& shader);
void Load(const std::string& path);
void Load(const std::string& name, const std::string& path);
Ref<Shader>& Get(const std::string& name);
private:
std::unordered_map<std::string, Ref<Shader>> m_Shaders;
};
}

View File

@ -111,13 +111,24 @@ DemoLayer::~DemoLayer()
void DemoLayer::OnAttach()
{
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_GridShader.reset(Prism::Shader::Create("assets/shaders/Grid.glsl"));
m_Mesh.reset(new Prism::Mesh("assets/models/m1911/m1911.fbx"));
m_SphereMesh.reset(new Prism::Mesh("assets/models/Sphere1m.fbx"));
m_PlaneMesh.reset(new Prism::Mesh("assets/models/Plane1m.fbx"));
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"));
@ -127,16 +138,12 @@ void DemoLayer::OnAttach()
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_Framebuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA16F));
m_FinalPresentBuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA8));
m_PBRMaterial.reset(new Prism::Material(m_SimplePBRShader));
float x = -4.0f;
float roughness = 0.0f;
for (int i = 0; i < 8; i++)
{
Prism::Ref<Prism::MaterialInstance> mi(new Prism::MaterialInstance(m_PBRMaterial));
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)));
@ -149,7 +156,7 @@ void DemoLayer::OnAttach()
roughness = 0.0f;
for (int i = 0; i < 8; i++)
{
Prism::Ref<Prism::MaterialInstance> mi(new Prism::MaterialInstance(m_PBRMaterial));
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)));
@ -212,10 +219,6 @@ void DemoLayer::OnUpdate(const Prism::TimeStep deltaTime)
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_QuadShader->SetMat4("u_InverseVP", inverse(viewProjection));
m_EnvironmentCubeMap->Bind(0);
@ -224,72 +227,72 @@ void DemoLayer::OnUpdate(const Prism::TimeStep deltaTime)
Renderer::DrawIndexed(m_IndexBuffer->GetCount(), false);
m_PBRMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color);
m_PBRMaterial->Set("u_Metalness", m_MetalnessInput.Value);
m_PBRMaterial->Set("u_Roughness", m_RoughnessInput.Value);
m_PBRMaterial->Set("u_ViewProjectionMatrix", viewProjection);
m_PBRMaterial->Set("u_ModelMatrix", scale(mat4(1.0f), vec3(m_MeshScale)));
m_PBRMaterial->Set("lights", m_Light);
m_PBRMaterial->Set("u_CameraPosition", m_Camera.GetPosition());
m_PBRMaterial->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f);
m_PBRMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f);
m_PBRMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f);
m_PBRMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f);
m_PBRMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f);
m_PBRMaterial->Set("u_EnvMapRotation", m_EnvMapRotation);
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_PBRMaterial->Set("u_EnvRadianceTex", m_EnvironmentCubeMap);
m_PBRMaterial->Set("u_EnvIrradianceTex", m_EnvironmentIrradiance);
m_PBRMaterial->Set("u_BRDFLUTTexture", m_BRDFLUT);
/*
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_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);
m_EnvironmentCubeMap->Bind(10);
m_EnvironmentIrradiance->Bind(11);
m_BRDFLUT->Bind(15);
m_SimplePBRShader->Bind();
*/
if (m_AlbedoInput.TextureMap)
m_PBRMaterial->Set("u_AlbedoTexture", m_AlbedoInput.TextureMap);
m_MeshMaterial->Set("u_AlbedoTexture", m_AlbedoInput.TextureMap);
if (m_NormalInput.TextureMap)
m_PBRMaterial->Set("u_NormalTexture", m_NormalInput.TextureMap);
m_MeshMaterial->Set("u_NormalTexture", m_NormalInput.TextureMap);
if (m_MetalnessInput.TextureMap)
m_PBRMaterial->Set("u_MetalnessTexture", m_MetalnessInput.TextureMap);
m_MeshMaterial->Set("u_MetalnessTexture", m_MetalnessInput.TextureMap);
if (m_RoughnessInput.TextureMap)
m_PBRMaterial->Set("u_RoughnessTexture", 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());
*/
}
}
@ -297,16 +300,13 @@ void DemoLayer::OnUpdate(const Prism::TimeStep deltaTime)
{
if (m_Mesh)
{
m_PBRMaterial->Bind();
m_Mesh->Render(deltaTime, m_SimplePBRShader.get());
m_Mesh->Render(deltaTime, scale(mat4(1.0f), vec3(m_MeshScale)), m_MeshMaterial);
}
}
m_GridShader->Bind();
m_GridShader->SetMat4("u_MVP", viewProjection * glm::scale(glm::mat4(1.0f), glm::vec3(16.0f)));
m_GridShader->SetFloat("u_Scale", m_GridScale);
m_GridShader->SetFloat("u_Res", m_GridSize);
m_PlaneMesh->Render(deltaTime, m_GridShader.get());
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();
@ -633,10 +633,20 @@ void DemoLayer::OnImGuiRender()
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::Image((void*)m_Framebuffer->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();
}

View File

@ -26,15 +26,16 @@ public:
private:
float m_ClearColor[4];
Prism::Ref<Prism::Shader> m_SimplePBRShader;
Prism::Scope<Prism::Shader> m_QuadShader;
Prism::Scope<Prism::Shader> m_HDRShader;
Prism::Scope<Prism::Shader> m_GridShader;
Prism::Scope<Prism::Mesh> m_Mesh;
Prism::Scope<Prism::Mesh> m_SphereMesh, m_PlaneMesh;
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::Material> m_PBRMaterial;
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;

View File

@ -174,9 +174,9 @@ void TestLayer::OnAttach()
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_SkyboxShader = Prism::Shader::Create("assets/shaders/quad.glsl");
m_Shader = Prism::Shader::Create("assets/shaders/demo.glsl");
m_HDRShader = Prism::Shader::Create("assets/shaders/hdr.glsl");
m_Mesh = std::make_unique<Prism::Mesh>("assets/meshes/cerberus.fbx");
}
@ -210,7 +210,7 @@ void TestLayer::OnUpdate(Prism::TimeStep deltaTime)
m_Shader->Bind();
m_Mesh->Render(deltaTime, m_Shader.get());
// m_Mesh->Render(deltaTime, m_Shader);
// m_VertexBuffer->Bind();
// m_IndexBuffer->Bind();

View File

@ -28,17 +28,17 @@ 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;
Prism::Ref<Prism::FrameBuffer> m_FrameBuffer, m_FinalPresentBuffer;
Prism::Ref<Prism::VertexBuffer> m_VertexBuffer;
Prism::Ref<Prism::IndexBuffer> m_IndexBuffer;
std::unique_ptr<Prism::TextureCube> m_SkyBoxTextureCube;
Prism::Ref<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::Ref<Prism::Shader> m_SkyboxShader;
Prism::Ref<Prism::Shader> m_Shader;
Prism::Ref<Prism::Shader> m_HDRShader;
Prism::Ref<Prism::Mesh> m_Mesh;
Prism::Camera m_Camera;

View File

@ -18,8 +18,8 @@ public:
virtual void OnInit() override
{
PushLayer(new TestLayer());
// PushLayer(new DemoLayer());
// PushLayer(new TestLayer());
PushLayer(new DemoLayer());
}
};

View File

@ -23,7 +23,6 @@ void main()
layout(location = 0) out vec4 color;
uniform sampler2D u_Texture;
uniform float u_Scale;
uniform float u_Res;

View File

@ -0,0 +1,321 @@
// -----------------------------
// -- 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);
}