add compute shader to auto calculate screen exposure, add SSBO impl
This commit is contained in:
63
Editor/assets/shaders/Exposure.glsl
Normal file
63
Editor/assets/shaders/Exposure.glsl
Normal file
@ -0,0 +1,63 @@
|
||||
#type compute
|
||||
#version 460 core
|
||||
layout(local_size_x = 1, local_size_y = 1) in;
|
||||
|
||||
layout(binding = 0, std430) buffer Histogram {
|
||||
uint bins[64];
|
||||
};
|
||||
layout(binding = 1, std430) buffer Exposure {
|
||||
float exposure;
|
||||
};
|
||||
|
||||
uniform float u_SpeedUp;
|
||||
uniform float u_SpeedDown;
|
||||
uniform float u_Key;
|
||||
uniform float u_LowPercent;
|
||||
uniform float u_HighPercent;
|
||||
uniform float u_MinExposure;
|
||||
uniform float u_MaxExposure;
|
||||
uniform float u_DeltaTime;
|
||||
uniform float u_LogMin;
|
||||
uniform float u_LogMax;
|
||||
|
||||
void main() {
|
||||
float currentExposure = exposure;
|
||||
|
||||
uint total = 0;
|
||||
uint prefix[64];
|
||||
for (int i = 0; i < 64; i++) {
|
||||
total += bins[i];
|
||||
prefix[i] = total;
|
||||
}
|
||||
|
||||
float lowCount = u_LowPercent * 0.01 * total;
|
||||
float highCount = u_HighPercent * 0.01 * total;
|
||||
int lowBin = 0, highBin = 63;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (prefix[i] < lowCount) lowBin = i + 1;
|
||||
if (prefix[i] < highCount) highBin = i + 1;
|
||||
}
|
||||
lowBin = clamp(lowBin, 0, 63);
|
||||
highBin = clamp(highBin, 0, 63);
|
||||
|
||||
float sumLum = 0.0;
|
||||
uint count = 0;
|
||||
for (int i = lowBin; i <= highBin; i++) {
|
||||
float t = (float(i) + 0.5) / 64.0;
|
||||
float logLum = u_LogMin + t * (u_LogMax - u_LogMin);
|
||||
float lum = exp2(logLum);
|
||||
sumLum += lum * float(bins[i]);
|
||||
count += bins[i];
|
||||
}
|
||||
float avgLum = count > 0 ? sumLum / count : 0.18;
|
||||
|
||||
float targetExposure = u_Key / max(avgLum, 0.0001);
|
||||
targetExposure = clamp(targetExposure, u_MinExposure, u_MaxExposure);
|
||||
|
||||
float speed = (targetExposure > currentExposure) ? u_SpeedUp : u_SpeedDown;
|
||||
float adaptFactor = 1.0 - exp(-speed * u_DeltaTime);
|
||||
float newExposure = mix(currentExposure, targetExposure, adaptFactor);
|
||||
newExposure = clamp(newExposure, u_MinExposure, u_MaxExposure);
|
||||
|
||||
exposure = newExposure;
|
||||
}
|
||||
26
Editor/assets/shaders/Histogram.glsl
Normal file
26
Editor/assets/shaders/Histogram.glsl
Normal file
@ -0,0 +1,26 @@
|
||||
#type compute
|
||||
#version 460 core
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
|
||||
layout(binding = 0) uniform sampler2D u_SceneColor;
|
||||
layout(binding = 1, std430) buffer Histogram {
|
||||
uint bins[64];
|
||||
};
|
||||
|
||||
uniform float u_LogMin;
|
||||
uniform float u_LogMax;
|
||||
|
||||
void main() {
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 size = textureSize(u_SceneColor, 0);
|
||||
if (texel.x >= size.x || texel.y >= size.y) return;
|
||||
|
||||
vec3 color = texelFetch(u_SceneColor, texel, 0).rgb;
|
||||
float lum = max(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0.0001);
|
||||
float logLum = log2(lum);
|
||||
|
||||
float invLogRange = 1.0 / (u_LogMax - u_LogMin);
|
||||
float t = (logLum - u_LogMin) * invLogRange;
|
||||
int bin = int(clamp(t * 64.0, 0.0, 63.0));
|
||||
atomicAdd(bins[bin], 1u);
|
||||
}
|
||||
@ -23,7 +23,14 @@ in vec2 v_TexCoord;
|
||||
|
||||
uniform sampler2DMS u_Texture;
|
||||
|
||||
uniform float u_Exposure;
|
||||
|
||||
uniform bool u_EnableAutoExposure;
|
||||
uniform float u_ManualExposure;
|
||||
layout(std430, binding = 2) buffer Exposure
|
||||
{
|
||||
float u_Exposure;
|
||||
};
|
||||
|
||||
uniform int u_TextureSamples;
|
||||
|
||||
uniform bool u_EnableBloom;
|
||||
@ -64,7 +71,10 @@ void main()
|
||||
color += bloomColor;
|
||||
}
|
||||
|
||||
color *= u_Exposure;
|
||||
if(u_EnableAutoExposure)
|
||||
color *= u_Exposure;
|
||||
else
|
||||
color *= u_ManualExposure;
|
||||
|
||||
// Reinhard tonemapping operator.
|
||||
// see: "Photographic Tone Reproduction for Digital Images", eq. 4
|
||||
|
||||
@ -44,7 +44,7 @@ namespace Prism
|
||||
|
||||
PhysicsMaterial() = default;
|
||||
|
||||
PhysicsMaterial(float staticFriction, float dynamicFriction, float bounciness)
|
||||
PhysicsMaterial(const float staticFriction, const float dynamicFriction, const float bounciness)
|
||||
: StaticFriction(staticFriction), DynamicFriction(dynamicFriction), Bounciness(bounciness)
|
||||
{
|
||||
Type = AssetType::PhysicsMaterial;
|
||||
|
||||
@ -41,11 +41,10 @@ namespace Prism
|
||||
ScriptEngine::Init("assets/scripts/ExampleApp.dll");
|
||||
Physics3D::Init();
|
||||
|
||||
Renderer::Init();
|
||||
Renderer::WaitAndRender();
|
||||
|
||||
AssetTypes::Init();
|
||||
AssetsManager::Init();
|
||||
Renderer::Init();
|
||||
Renderer::WaitAndRender();
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
|
||||
@ -27,6 +27,8 @@ namespace Prism
|
||||
return Elapsed() * 1000.0f;
|
||||
}
|
||||
|
||||
operator float() const { return Elapsed(); }
|
||||
|
||||
private:
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_Start;
|
||||
};
|
||||
|
||||
@ -188,4 +188,15 @@ namespace Prism
|
||||
{
|
||||
glLineWidth(thickness);
|
||||
}
|
||||
|
||||
void RendererAPI::DispatchCompute(int x, int y, int z)
|
||||
{
|
||||
glDispatchCompute(x, y, z);
|
||||
}
|
||||
|
||||
void RendererAPI::MemoryBarrier(const int barrier)
|
||||
{
|
||||
glMemoryBarrier(barrier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
73
Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.cpp
Normal file
73
Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// Created by Atdunbg on 2026/3/12.
|
||||
//
|
||||
|
||||
#include "OpenGLStorageBuffer.h"
|
||||
|
||||
#include "Prism/Renderer/Renderer.h"
|
||||
#include <glad/glad.h>
|
||||
|
||||
namespace Prism {
|
||||
|
||||
static GLenum UsageToGL(const StorageBufferUsage usage)
|
||||
{
|
||||
switch (usage)
|
||||
{
|
||||
case StorageBufferUsage::Static: return GL_STATIC_DRAW;
|
||||
case StorageBufferUsage::Dynamic: return GL_DYNAMIC_DRAW;
|
||||
case StorageBufferUsage::Stream: return GL_STREAM_DRAW;
|
||||
case StorageBufferUsage::DynamicCopy: return GL_DYNAMIC_COPY;
|
||||
default: return GL_DYNAMIC_DRAW;
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLStorageBuffer::OpenGLStorageBuffer(uint32_t size, const void* data, StorageBufferUsage usage)
|
||||
: m_Size(size), m_Usage(usage)
|
||||
{
|
||||
Renderer::Submit([this, data]() {
|
||||
glCreateBuffers(1, &m_RendererID);
|
||||
glNamedBufferData(m_RendererID, m_Size, data, UsageToGL(m_Usage));
|
||||
});
|
||||
}
|
||||
|
||||
OpenGLStorageBuffer::~OpenGLStorageBuffer()
|
||||
{
|
||||
Renderer::Submit([this]() {
|
||||
glDeleteBuffers(1, &m_RendererID);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLStorageBuffer::Bind() const
|
||||
{
|
||||
Renderer::Submit([this]() {
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_RendererID);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLStorageBuffer::Unbind() const
|
||||
{
|
||||
Renderer::Submit([]() {
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLStorageBuffer::BindBase(uint32_t index) const
|
||||
{
|
||||
Renderer::Submit([this, index]() {
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index, m_RendererID);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLStorageBuffer::SetData(const void* data, uint32_t size, uint32_t offset)
|
||||
{
|
||||
Renderer::Submit([this, data, size, offset]() {
|
||||
glNamedBufferSubData(m_RendererID, offset, size, data);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLStorageBuffer::GetData(void* outData, const uint32_t size, const uint32_t offset) const
|
||||
{
|
||||
glGetNamedBufferSubData(m_RendererID, offset, size, outData);
|
||||
}
|
||||
|
||||
}
|
||||
35
Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.h
Normal file
35
Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.h
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// Created by Atdunbg on 2026/3/12.
|
||||
//
|
||||
|
||||
#ifndef PRISM_OPENGLSTORAGEBUFFER_H
|
||||
#define PRISM_OPENGLSTORAGEBUFFER_H
|
||||
|
||||
#include "Prism/Renderer/StorageBuffer.h"
|
||||
|
||||
namespace Prism {
|
||||
|
||||
class OpenGLStorageBuffer : public StorageBuffer
|
||||
{
|
||||
public:
|
||||
OpenGLStorageBuffer(uint32_t size, const void* data, StorageBufferUsage usage);
|
||||
virtual ~OpenGLStorageBuffer();
|
||||
|
||||
virtual void Bind() const override;
|
||||
virtual void Unbind() const override;
|
||||
virtual void BindBase(uint32_t index) const override;
|
||||
virtual void SetData(const void* data, uint32_t size, uint32_t offset = 0) override;
|
||||
virtual void GetData(void* outData, uint32_t size, uint32_t offset = 0) const override;
|
||||
virtual uint32_t GetRendererID() const override { return m_RendererID; }
|
||||
virtual uint32_t GetSize() const override { return m_Size; }
|
||||
|
||||
private:
|
||||
uint32_t m_RendererID = 0;
|
||||
uint32_t m_Size = 0;
|
||||
StorageBufferUsage m_Usage;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //PRISM_OPENGLSTORAGEBUFFER_H
|
||||
@ -253,6 +253,22 @@ namespace Prism
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::DispatchCompute(int x, int y, int z)
|
||||
{
|
||||
Submit([x, y, z]()
|
||||
{
|
||||
RendererAPI::DispatchCompute(x, y, z);
|
||||
});
|
||||
}
|
||||
|
||||
void Renderer::MemoryBarrier(int barrier)
|
||||
{
|
||||
Submit([barrier]()
|
||||
{
|
||||
RendererAPI::MemoryBarrier(barrier);
|
||||
});
|
||||
}
|
||||
|
||||
void Renderer::DrawAABB(const AABB& aabb, const glm::mat4& transform, const glm::vec4& color)
|
||||
{
|
||||
glm::vec4 min = { aabb.Min.x, aabb.Min.y, aabb.Min.z, 1.0f };
|
||||
|
||||
@ -59,6 +59,9 @@ namespace Prism
|
||||
|
||||
static void DrawAABB(const AABB& aabb, const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f));
|
||||
static void DrawAABB(const Ref<Mesh>& mesh,const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f));
|
||||
static void DispatchCompute(int x, int y, int z);
|
||||
static void MemoryBarrier(int barrier);
|
||||
|
||||
private:
|
||||
static RenderCommandQueue& GetRenderCommandQueue();
|
||||
};
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "Renderer.h"
|
||||
#include "Renderer2D.h"
|
||||
#include "SceneRenderer.h"
|
||||
#include "StorageBuffer.h"
|
||||
#include "Prism/Scene/Components.h"
|
||||
|
||||
namespace Prism
|
||||
@ -39,19 +40,29 @@ namespace Prism
|
||||
Ref<RenderPass> BloomBlurPass[2];
|
||||
Ref<RenderPass> BloomBlendPass;
|
||||
|
||||
// Auto exposure
|
||||
struct AutoExposureData
|
||||
{
|
||||
Ref<RenderPass> LuminancePass;
|
||||
|
||||
float CurrentExposure = 1.0f;
|
||||
bool EnableAutoExposure = true;
|
||||
float Key = 0.11f; // middle gray
|
||||
float AdaptationSpeed = 5.0f; // stops per second
|
||||
|
||||
float Key = 0.11f;
|
||||
Timer ExposureTimer;
|
||||
float MaxExposure = 5.0f;
|
||||
}AutoExposureData;
|
||||
float MinExposure = 0.01f;
|
||||
|
||||
// 直方图模式参数
|
||||
float SpeedUp = 5.0f;
|
||||
float SpeedDown = 2.0f;
|
||||
float LowPercent = 70.0f;
|
||||
float HighPercent = 95.0f;
|
||||
float LogMin = -4.0f;
|
||||
float LogMax = 4.0f;
|
||||
|
||||
Ref<StorageBuffer> HistogramSSBO;
|
||||
Ref<StorageBuffer> ExposureSSBO;
|
||||
Ref<Shader> HistogramCS;
|
||||
Ref<Shader> ExposureCS;
|
||||
} AutoExposureData;
|
||||
|
||||
Ref<Shader> ShadowMapShader, ShadowMapAnimShader;
|
||||
Ref<RenderPass> ShadowMapRenderPass;
|
||||
@ -135,65 +146,100 @@ namespace Prism
|
||||
|
||||
void Renderer3D::Init()
|
||||
{
|
||||
FramebufferSpecification geoFramebufferSpec;
|
||||
geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::DEPTH24STENCIL8 };
|
||||
geoFramebufferSpec.Samples = 8;
|
||||
geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
|
||||
//////////////// GeoPass ////////////////
|
||||
{
|
||||
FramebufferSpecification geoFramebufferSpec;
|
||||
geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::DEPTH24STENCIL8 };
|
||||
geoFramebufferSpec.Samples = 8;
|
||||
geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
|
||||
|
||||
RenderPassSpecification geoRenderPassSpec;
|
||||
geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec);
|
||||
s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec);
|
||||
RenderPassSpecification geoRenderPassSpec;
|
||||
geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec);
|
||||
s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec);
|
||||
}
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
FramebufferSpecification compFramebufferSpec;
|
||||
compFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 , FramebufferTextureFormat::RGBA8};
|
||||
compFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
|
||||
|
||||
RenderPassSpecification compRenderPassSpec;
|
||||
compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec);
|
||||
s_Data.CompositePass = RenderPass::Create(compRenderPassSpec);
|
||||
*/
|
||||
//////////////// BloomPass ////////////////
|
||||
{
|
||||
FramebufferSpecification bloomBlurFramebufferSpec;
|
||||
bloomBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F , FramebufferTextureFormat::RGBA8};
|
||||
bloomBlurFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
|
||||
|
||||
FramebufferSpecification bloomBlurFramebufferSpec;
|
||||
bloomBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F , FramebufferTextureFormat::RGBA8};
|
||||
bloomBlurFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
|
||||
RenderPassSpecification bloomBlurRenderPassSpec;
|
||||
bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec);
|
||||
s_Data.BloomBlurPass[0] = RenderPass::Create(bloomBlurRenderPassSpec);
|
||||
bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec);
|
||||
s_Data.BloomBlurPass[1] = RenderPass::Create(bloomBlurRenderPassSpec);
|
||||
|
||||
RenderPassSpecification bloomBlurRenderPassSpec;
|
||||
bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec);
|
||||
s_Data.BloomBlurPass[0] = RenderPass::Create(bloomBlurRenderPassSpec);
|
||||
bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec);
|
||||
s_Data.BloomBlurPass[1] = RenderPass::Create(bloomBlurRenderPassSpec);
|
||||
FramebufferSpecification bloomBlendFramebufferSpec;
|
||||
bloomBlendFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 };
|
||||
bloomBlendFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
|
||||
|
||||
FramebufferSpecification bloomBlendFramebufferSpec;
|
||||
bloomBlendFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 };
|
||||
bloomBlendFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f };
|
||||
RenderPassSpecification bloomBlendRenderPassSpec;
|
||||
bloomBlendRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlendFramebufferSpec);
|
||||
s_Data.BloomBlendPass = RenderPass::Create(bloomBlendRenderPassSpec);
|
||||
|
||||
RenderPassSpecification bloomBlendRenderPassSpec;
|
||||
bloomBlendRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlendFramebufferSpec);
|
||||
s_Data.BloomBlendPass = RenderPass::Create(bloomBlendRenderPassSpec);
|
||||
s_Data.BloomBlurShader = Shader::Create("assets/shaders/BloomBlur.glsl");
|
||||
s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl");
|
||||
}
|
||||
/////////////////////////////////////////////
|
||||
|
||||
//////////////// AutoExposure ////////////////
|
||||
{
|
||||
// Luminance pass: used to compute average scene luminance (for auto exposure)
|
||||
FramebufferSpecification luminanceFramebufferSpec;
|
||||
luminanceFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F };
|
||||
luminanceFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
RenderPassSpecification luminanceRenderPassSpec;
|
||||
luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec);
|
||||
s_Data.AutoExposureData.LuminancePass = RenderPass::Create(luminanceRenderPassSpec);
|
||||
|
||||
constexpr uint32_t histogramSize = 64 * sizeof(uint32_t);
|
||||
std::vector<uint32_t> zeros(64, 0);
|
||||
s_Data.AutoExposureData.HistogramSSBO = StorageBuffer::Create(histogramSize, zeros.data());
|
||||
float initialExposure = 1.0f;
|
||||
s_Data.AutoExposureData.ExposureSSBO = StorageBuffer::Create(sizeof(float), &initialExposure);
|
||||
s_Data.AutoExposureData.HistogramCS = Shader::Create("assets/shaders/Histogram.glsl");
|
||||
s_Data.AutoExposureData.ExposureCS = Shader::Create("assets/shaders/Exposure.glsl");
|
||||
}
|
||||
/////////////////////////////////////////////
|
||||
|
||||
|
||||
//////////////// ShadowMapPass ////////////////
|
||||
{
|
||||
FramebufferSpecification shadowMapFramebufferSpec;
|
||||
shadowMapFramebufferSpec.Width = 4096;
|
||||
shadowMapFramebufferSpec.Height = 4096;
|
||||
shadowMapFramebufferSpec.Attachments = { FramebufferTextureFormat::DEPTH32F };
|
||||
shadowMapFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
shadowMapFramebufferSpec.NoResize = true;
|
||||
|
||||
RenderPassSpecification shadowMapRenderPassSpec;
|
||||
shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec);
|
||||
s_Data.ShadowMapRenderPass = RenderPass::Create(shadowMapRenderPassSpec);
|
||||
|
||||
Renderer::Submit([]()
|
||||
{
|
||||
glGenSamplers(1, &s_Data.ShadowMapSampler);
|
||||
|
||||
// Setup the shadowMap depth sampler
|
||||
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
});
|
||||
}
|
||||
/////////////////////////////////////////////
|
||||
|
||||
s_Data.CompositeShader = Shader::Create("assets/shaders/SceneComposite.glsl");
|
||||
s_Data.BloomBlurShader = Shader::Create("assets/shaders/BloomBlur.glsl");
|
||||
s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl");
|
||||
s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga");
|
||||
|
||||
// Luminance pass: used to compute average scene luminance (for auto exposure)
|
||||
FramebufferSpecification luminanceFramebufferSpec;
|
||||
luminanceFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F };
|
||||
luminanceFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
RenderPassSpecification luminanceRenderPassSpec;
|
||||
luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec);
|
||||
s_Data.AutoExposureData.LuminancePass = RenderPass::Create(luminanceRenderPassSpec);
|
||||
|
||||
// Grid
|
||||
// const auto gridShader = Shader::Create("assets/shaders/Grid.glsl");
|
||||
const auto gridShader = Shader::Create("assets/shaders/InfiniteGrid.glsl");
|
||||
s_Data.GridData.GridMaterial = MaterialInstance::Create(Material::Create(gridShader));
|
||||
// constexpr float gridScale = 16.025f;
|
||||
// constexpr float gridSize = 0.025f;
|
||||
// s_Data.GridMaterial->Set("u_Scale", gridScale);
|
||||
// s_Data.GridMaterial->Set("u_Res", gridSize);
|
||||
s_Data.GridData.GridMaterial->SetFlag(MaterialFlag::TwoSided, true);
|
||||
|
||||
// outline
|
||||
@ -201,41 +247,18 @@ namespace Prism
|
||||
s_Data.OutlineMaterial = MaterialInstance::Create(Material::Create(outlineShader));
|
||||
s_Data.OutlineMaterial->SetFlag(MaterialFlag::DepthTest, false);
|
||||
|
||||
// outlineAnim
|
||||
auto outlineAnimShader = Shader::Create("assets/shaders/Outline_Anim.glsl");
|
||||
s_Data.OutlineAnimMaterial = MaterialInstance::Create(Material::Create(outlineAnimShader));
|
||||
s_Data.OutlineAnimMaterial->SetFlag(MaterialFlag::DepthTest, false);
|
||||
|
||||
// Collider
|
||||
const auto colliderShader = Shader::Create("assets/shaders/Collider.glsl");
|
||||
s_Data.ColliderMaterial = MaterialInstance::Create(Material::Create(colliderShader));
|
||||
s_Data.ColliderMaterial->SetFlag(MaterialFlag::DepthTest, false);
|
||||
|
||||
auto outlineAnimShader = Shader::Create("assets/shaders/Outline_Anim.glsl");
|
||||
s_Data.OutlineAnimMaterial = MaterialInstance::Create(Material::Create(outlineAnimShader));
|
||||
s_Data.OutlineAnimMaterial->SetFlag(MaterialFlag::DepthTest, false);
|
||||
|
||||
// Shadow Map
|
||||
s_Data.ShadowMapShader = Shader::Create("assets/shaders/ShadowMap.glsl");
|
||||
s_Data.ShadowMapAnimShader = Shader::Create("assets/shaders/ShadowMap_Anim.glsl");
|
||||
|
||||
FramebufferSpecification shadowMapFramebufferSpec;
|
||||
shadowMapFramebufferSpec.Width = 4096;
|
||||
shadowMapFramebufferSpec.Height = 4096;
|
||||
shadowMapFramebufferSpec.Attachments = { FramebufferTextureFormat::DEPTH32F };
|
||||
shadowMapFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
shadowMapFramebufferSpec.NoResize = true;
|
||||
|
||||
RenderPassSpecification shadowMapRenderPassSpec;
|
||||
shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec);
|
||||
s_Data.ShadowMapRenderPass = RenderPass::Create(shadowMapRenderPassSpec);
|
||||
|
||||
Renderer::Submit([]()
|
||||
{
|
||||
glGenSamplers(1, &s_Data.ShadowMapSampler);
|
||||
|
||||
// Setup the shadowmap depth sampler
|
||||
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void Renderer3D::SetViewportSize(uint32_t width, uint32_t height)
|
||||
@ -429,7 +452,10 @@ namespace Prism
|
||||
Renderer::BeginRenderPass(outRenderPass);
|
||||
|
||||
s_Data.CompositeShader->Bind();
|
||||
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
|
||||
|
||||
s_Data.AutoExposureData.ExposureSSBO->BindBase(2);
|
||||
s_Data.CompositeShader->SetBool("u_EnableAutoExposure",s_Data.AutoExposureData.EnableAutoExposure);
|
||||
s_Data.CompositeShader->SetFloat("u_ManualExposure", s_Data.SceneData.SceneCamera.Camera.GetExposure());
|
||||
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
|
||||
s_Data.CompositeShader->SetFloat("u_EnableBloom", s_Data.EnableBloom);
|
||||
|
||||
@ -790,125 +816,72 @@ namespace Prism
|
||||
|
||||
void Renderer3D::AutoExposurePass()
|
||||
{
|
||||
if (!s_Data.AutoExposureData.EnableAutoExposure)
|
||||
auto& ae = s_Data.AutoExposureData;
|
||||
if (!ae.EnableAutoExposure)
|
||||
return;
|
||||
|
||||
auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer;
|
||||
auto dstFB = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer;
|
||||
if (!srcFB || !dstFB)
|
||||
return;
|
||||
auto dstFB = ae.LuminancePass->GetSpecification().TargetFramebuffer;
|
||||
if (!srcFB || !dstFB) return;
|
||||
|
||||
const uint32_t dstID = dstFB->GetColorAttachmentRendererID();
|
||||
const uint32_t width = dstFB->GetWidth();
|
||||
const uint32_t height = dstFB->GetHeight();
|
||||
float dt = ae.ExposureTimer;
|
||||
ae.ExposureTimer.Reset();
|
||||
|
||||
Renderer::Submit([dstID, width, height, srcFB]() mutable
|
||||
{
|
||||
// Use framebuffer blit to resolve multisampled source into non-multisampled luminance target.
|
||||
const GLuint srcFBO = srcFB->GetRendererID();
|
||||
const GLuint dstFBO = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer->GetRendererID();
|
||||
|
||||
// Bind read/draw FBOs and blit color attachment 0
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO);
|
||||
Renderer::Submit([srcFB, dstFB, logMin = ae.LogMin, logMax = ae.LogMax, histCS = ae.HistogramCS, histSSBO = ae.HistogramSSBO]() {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFB->GetRendererID());
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFB->GetRendererID());
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
// Source size — try to use srcFB dimensions if available
|
||||
const int srcWidth = srcFB->GetWidth();
|
||||
const int srcHeight = srcFB->GetHeight();
|
||||
|
||||
glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
// Unbind
|
||||
glBlitFramebuffer(0, 0, srcFB->GetWidth(), srcFB->GetHeight(),
|
||||
0, 0, dstFB->GetWidth(), dstFB->GetHeight(),
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
// Generate mipmaps so the smallest mip is the average color
|
||||
glGenerateTextureMipmap(dstID);
|
||||
const uint32_t zero = 0;
|
||||
glClearNamedBufferData(histSSBO->GetRendererID(), GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero);
|
||||
|
||||
// Determine highest mip level
|
||||
int maxLevel = (int)std::floor(std::log2((float)std::max(width, height)));
|
||||
if (maxLevel < 0) maxLevel = 0;
|
||||
glUseProgram(histCS->GetRendererID());
|
||||
glBindTextureUnit(0, dstFB->GetColorAttachmentRendererID());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, histSSBO->GetRendererID());
|
||||
|
||||
const GLint locLogMin = glGetUniformLocation(histCS->GetRendererID(), "u_LogMin");
|
||||
const GLint locLogMax = glGetUniformLocation(histCS->GetRendererID(), "u_LogMax");
|
||||
if (locLogMin != -1) glProgramUniform1f(histCS->GetRendererID(), locLogMin, logMin);
|
||||
if (locLogMax != -1) glProgramUniform1f(histCS->GetRendererID(), locLogMax, logMax);
|
||||
|
||||
GLint levelWidth = 0, levelHeight = 0;
|
||||
glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_WIDTH, &levelWidth);
|
||||
glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_HEIGHT, &levelHeight);
|
||||
|
||||
if (levelWidth == 0 || levelHeight == 0) return;
|
||||
|
||||
const int bufSize = levelWidth * levelHeight * 4 * sizeof(float);
|
||||
std::vector<float> pixelData(bufSize / sizeof(float));
|
||||
|
||||
glGetTextureImage(dstID, maxLevel, GL_RGBA, GL_FLOAT, bufSize, pixelData.data());
|
||||
|
||||
// Sanitize pixel values (handle NaN/Inf or negative values coming from GPU)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (!std::isfinite(pixelData[i]) || pixelData[i] < 0.0f)
|
||||
pixelData[i] = 0.0f;
|
||||
}
|
||||
|
||||
// Convert to luminance
|
||||
float lum = 0.2126f * pixelData[0] + 0.7152f * pixelData[1] + 0.0722f * pixelData[2];
|
||||
if (!std::isfinite(lum) || lum <= 0.0f)
|
||||
lum = 1e-6f; // fallback minimum luminance
|
||||
|
||||
// Compute desired exposure (simple key/avg approach)
|
||||
const float key = s_Data.AutoExposureData.Key;
|
||||
constexpr float minLum = 1e-6f;
|
||||
float desiredExposure = key / std::max(lum, minLum);
|
||||
desiredExposure = std::clamp(desiredExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure);
|
||||
|
||||
|
||||
// Adapt exposure over time (exponential)
|
||||
const float dt = s_Data.AutoExposureData.ExposureTimer.ElapsedMillis() / 1000.0f;
|
||||
s_Data.AutoExposureData.ExposureTimer.Reset();
|
||||
const float tau = s_Data.AutoExposureData.AdaptationSpeed;
|
||||
const float adaptFactor = 1.0f - std::exp(-tau * dt);
|
||||
s_Data.AutoExposureData.CurrentExposure = s_Data.AutoExposureData.CurrentExposure + (desiredExposure - s_Data.AutoExposureData.CurrentExposure) * adaptFactor;
|
||||
s_Data.AutoExposureData.CurrentExposure = std::clamp(s_Data.AutoExposureData.CurrentExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure);
|
||||
|
||||
// Write exposure directly into composite shader program uniform so the subsequent composite pass uses it
|
||||
/*
|
||||
if (const GLuint prog = s_Data.CompositeShader->GetRendererID())
|
||||
{
|
||||
const GLint loc = glGetUniformLocation(prog, "u_Exposure");
|
||||
if (loc >= 0)
|
||||
glProgramUniform1f(prog, loc, s_Data.CurrentExposure);
|
||||
}
|
||||
*/
|
||||
const uint32_t groupsX = (dstFB->GetWidth() + 15) / 16;
|
||||
const uint32_t groupsY = (dstFB->GetHeight() + 15) / 16;
|
||||
glDispatchCompute(groupsX, groupsY, 1);
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
});
|
||||
|
||||
Renderer::Submit([&ae, dt]() {
|
||||
glUseProgram(ae.ExposureCS->GetRendererID());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ae.HistogramSSBO->GetRendererID());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ae.ExposureSSBO->GetRendererID());
|
||||
|
||||
auto setUniform = [&](const char* name, const float value) {
|
||||
const GLint loc = glGetUniformLocation(ae.ExposureCS->GetRendererID(), name);
|
||||
if (loc != -1) glProgramUniform1f(ae.ExposureCS->GetRendererID(), loc, value);
|
||||
};
|
||||
|
||||
setUniform("u_SpeedUp", ae.SpeedUp);
|
||||
setUniform("u_SpeedDown", ae.SpeedDown);
|
||||
setUniform("u_Key", ae.Key);
|
||||
setUniform("u_LowPercent", ae.LowPercent);
|
||||
setUniform("u_HighPercent", ae.HighPercent);
|
||||
setUniform("u_MinExposure", ae.MinExposure);
|
||||
setUniform("u_MaxExposure", ae.MaxExposure);
|
||||
setUniform("u_DeltaTime", dt);
|
||||
setUniform("u_LogMin", ae.LogMin);
|
||||
setUniform("u_LogMax", ae.LogMax);
|
||||
|
||||
glDispatchCompute(1, 1, 1);
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
void Renderer3D::CompositePass()
|
||||
{
|
||||
auto& compositeBuffer = s_Data.CompositePass->GetSpecification().TargetFramebuffer;
|
||||
|
||||
Renderer::BeginRenderPass(s_Data.CompositePass);
|
||||
s_Data.CompositeShader->Bind();
|
||||
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
|
||||
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
|
||||
// s_Data.CompositeShader->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight()));
|
||||
// s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint);
|
||||
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
|
||||
// s_Data.CompositeShader->SetFloat("u_BloomThreshold", s_Data.BloomThreshold);
|
||||
s_Data.CompositeShader->SetFloat("u_EnableBloom", s_Data.EnableBloom);
|
||||
|
||||
s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture();
|
||||
Renderer::Submit([]()
|
||||
{
|
||||
glBindTextureUnit(1, s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID());
|
||||
});
|
||||
|
||||
Renderer::SubmitFullscreenQuad(nullptr);
|
||||
Renderer::EndRenderPass();
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
void Renderer3D::BloomBlurPass()
|
||||
{
|
||||
@ -1111,7 +1084,7 @@ namespace Prism
|
||||
|
||||
// TODO: this will not be hardcode
|
||||
const glm::vec3 lightDir = glm::normalize(directionalLights[0].Direction); // 光线方向(从光源指向场景)
|
||||
glm::vec3 lightPos = lightDir * 10.0f;
|
||||
glm::vec3 lightPos = lightDir * 100.0f;
|
||||
|
||||
glm::mat4 lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
@ -1225,6 +1198,7 @@ namespace Prism
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
|
||||
/*
|
||||
if (UI::BeginTreeNode("Auto Exposure", false))
|
||||
{
|
||||
UI::BeginPropertyGrid();
|
||||
@ -1236,6 +1210,36 @@ namespace Prism
|
||||
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
*/
|
||||
|
||||
if (UI::BeginTreeNode("Auto Exposure", false))
|
||||
{
|
||||
UI::BeginPropertyGrid();
|
||||
UI::Property("Enable Auto Exposure", s_Data.AutoExposureData.EnableAutoExposure);
|
||||
if (s_Data.AutoExposureData.EnableAutoExposure)
|
||||
{
|
||||
UI::Property("Key (middle gray)", s_Data.AutoExposureData.Key, 0.001f, 0.001f, 2.5f);
|
||||
|
||||
UI::Separator();
|
||||
UI::Property("Speed Up (brighten)", s_Data.AutoExposureData.SpeedUp, 0.1f, 0.1f, 20.0f);
|
||||
UI::Property("Speed Down (darken)", s_Data.AutoExposureData.SpeedDown, 0.1f, 0.1f, 20.0f);
|
||||
|
||||
UI::Separator();
|
||||
UI::Property("Low Percent", s_Data.AutoExposureData.LowPercent, 1.0f, 0.0f, 100.0f);
|
||||
UI::Property("High Percent", s_Data.AutoExposureData.HighPercent, 1.0f, 0.0f, 100.0f);
|
||||
|
||||
UI::Separator();
|
||||
UI::Property("Min Exposure", s_Data.AutoExposureData.MinExposure, 0.01f, 0.0f, 10.0f);
|
||||
UI::Property("Max Exposure", s_Data.AutoExposureData.MaxExposure, 0.1f, 0.0f, 20.0f);
|
||||
|
||||
UI::Separator();
|
||||
UI::Property("Log Min", s_Data.AutoExposureData.LogMin, 0.1f, -10.0f, 10.0f);
|
||||
UI::Property("Log Max", s_Data.AutoExposureData.LogMax, 0.1f, -10.0f, 10.0f);
|
||||
}
|
||||
|
||||
UI::EndPropertyGrid();
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
|
||||
|
||||
ImGui::End();
|
||||
|
||||
@ -52,6 +52,9 @@ namespace Prism
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
static void DispatchCompute(int x, int y, int z);
|
||||
static void MemoryBarrier(int barrier);
|
||||
|
||||
private:
|
||||
static RendererAPIType s_CurrentRendererAPI;
|
||||
};
|
||||
|
||||
25
Prism/src/Prism/Renderer/StorageBuffer.cpp
Normal file
25
Prism/src/Prism/Renderer/StorageBuffer.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by Atdunbg on 2026/3/12.
|
||||
//
|
||||
|
||||
#include "StorageBuffer.h"
|
||||
|
||||
#include "RendererAPI.h"
|
||||
#include "Prism/Platform/OpenGL/OpenGLStorageBuffer.h"
|
||||
|
||||
namespace Prism
|
||||
{
|
||||
Ref<StorageBuffer> StorageBuffer::Create(uint32_t size, const void* data, StorageBufferUsage usage)
|
||||
{
|
||||
Ref<StorageBuffer> result = nullptr;
|
||||
|
||||
switch (RendererAPI::Current())
|
||||
{
|
||||
case RendererAPIType::None: return nullptr;
|
||||
case RendererAPIType::OpenGL: result = Ref<OpenGLStorageBuffer>::Create(size, data, usage); break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
42
Prism/src/Prism/Renderer/StorageBuffer.h
Normal file
42
Prism/src/Prism/Renderer/StorageBuffer.h
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// Created by Atdunbg on 2026/3/12.
|
||||
//
|
||||
|
||||
#ifndef PRISM_STORAGEBUFFER_H
|
||||
#define PRISM_STORAGEBUFFER_H
|
||||
#include "Prism/Core/Ref.h"
|
||||
|
||||
namespace Prism
|
||||
{
|
||||
enum class StorageBufferUsage
|
||||
{
|
||||
Static = 0,
|
||||
Dynamic,
|
||||
Stream,
|
||||
DynamicCopy
|
||||
};
|
||||
|
||||
class StorageBuffer : public RefCounted
|
||||
{
|
||||
public:
|
||||
virtual ~StorageBuffer() = default;
|
||||
|
||||
static Ref<StorageBuffer> Create(uint32_t size, const void* data = nullptr, StorageBufferUsage usage = StorageBufferUsage::Dynamic);
|
||||
|
||||
virtual void Bind() const = 0;
|
||||
virtual void Unbind() const = 0;
|
||||
|
||||
virtual void BindBase(uint32_t index) const = 0;
|
||||
|
||||
virtual void SetData(const void* data, uint32_t size, uint32_t offset = 0) = 0;
|
||||
virtual void GetData(void* outData, uint32_t size, uint32_t offset = 0) const = 0;
|
||||
|
||||
virtual uint32_t GetRendererID() const = 0;
|
||||
virtual uint32_t GetSize() const = 0;
|
||||
|
||||
protected:
|
||||
StorageBuffer() = default;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //PRISM_STORAGEBUFFER_H
|
||||
22
README.md
22
README.md
@ -95,19 +95,19 @@ ScriptComponent
|
||||
2. **配置依赖**
|
||||
- PhysX SDK: Prism/CmakeLists.txt内记录了是用的PhysX的构建配置,建议手动按照配置构建SDK,构建好后无需移动文件,项目可以自动链接库
|
||||
|
||||
3. **编译**
|
||||
**Cmake**:
|
||||
```bash
|
||||
cd Prism
|
||||
cmake -B build -G Ninja
|
||||
cmake --build build --target PrismEditor --config Release -j10
|
||||
```
|
||||
3. **编译**:
|
||||
- **Cmake**:
|
||||
```bash
|
||||
cd Prism
|
||||
cmake -B build -G Ninja
|
||||
cmake --build build --target PrismEditor --config Release -j10
|
||||
```
|
||||
|
||||
**Visual Studio**:
|
||||
visual Studio启用Cmake。 然后点击project下拉框,找到 PrismEditor,构建。
|
||||
**Visual Studio**:
|
||||
- visual Studio启用Cmake。 然后点击project下拉框,找到 PrismEditor,构建。
|
||||
|
||||
**JetBrain Clion**:
|
||||
clion 会自动识别Cmake, 运行调试配置找到PrismEditor构建。
|
||||
**JetBrain Clion**:
|
||||
- clion 会自动识别Cmake, 运行调试配置找到PrismEditor构建。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user