Skip to content

Commit b84cfd2

Browse files
committed
Enhanced shader class with LUT caching for specular reflection to optimize computation and eliminate redundancy. Added copy/move constructors for thread safety
Signed-off-by: ZhouFANG <indevn@outlook.com>
1 parent e81bcff commit b84cfd2

2 files changed

Lines changed: 122 additions & 6 deletions

File tree

src/include/shader.hpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#ifndef SIMPLERENDER_SRC_INCLUDE_SHADER_HPP_
22
#define SIMPLERENDER_SRC_INCLUDE_SHADER_HPP_
33

4+
#include <array>
5+
#include <bit>
6+
#include <shared_mutex>
7+
#include <unordered_map>
48
#include <variant>
59

610
#include "light.h"
@@ -12,6 +16,8 @@ namespace simple_renderer {
1216
using UniformValue = std::variant<int, float, Vector2f, Vector3f, Vector4f,
1317
Matrix3f, Matrix4f, Material, Light>;
1418

19+
inline constexpr size_t kSpecularLutResolution = 256;
20+
1521
class UniformBuffer {
1622
public:
1723
template <typename T>
@@ -85,17 +91,21 @@ struct FragmentUniformCache {
8591
bool derived_valid = false;
8692
};
8793

94+
struct SpecularLUT {
95+
std::array<float, kSpecularLutResolution> values{};
96+
};
97+
8898
/**
8999
* @brief Shader Class 着色器类
90100
*
91101
*/
92102
class Shader {
93103
public:
94104
Shader() = default;
95-
Shader(const Shader &shader) = default;
96-
Shader(Shader &&shader) = default;
97-
auto operator=(const Shader &shader) -> Shader & = default;
98-
auto operator=(Shader &&shader) -> Shader & = default;
105+
Shader(const Shader &shader);
106+
Shader(Shader &&shader) noexcept;
107+
auto operator=(const Shader &shader) -> Shader &;
108+
auto operator=(Shader &&shader) noexcept -> Shader &;
99109
virtual ~Shader() = default;
100110

101111
// Input Data -> Vertex Shader -> Screen Space Coordiante
@@ -127,6 +137,8 @@ class Shader {
127137
SharedDataInShader sharedDataInShader_;
128138
VertexUniformCache vertex_uniform_cache_;
129139
FragmentUniformCache fragment_uniform_cache_;
140+
mutable std::unordered_map<uint32_t, SpecularLUT> specular_lut_cache_;
141+
mutable std::shared_mutex specular_cache_mutex_;
130142

131143
void UpdateMatrixCache(const std::string &name, const Matrix4f &value);
132144
void UpdateFragmentCache(const std::string &name, const Light &value);
@@ -136,6 +148,11 @@ class Shader {
136148
void PrepareVertexUniformCache();
137149
void PrepareFragmentUniformCache();
138150

151+
// LUT相关
152+
[[nodiscard]] auto BuildSpecularLUT(float shininess) const -> SpecularLUT;
153+
[[nodiscard]] auto GetSpecularLUT(float shininess) const -> const SpecularLUT &;
154+
[[nodiscard]] auto EvaluateSpecular(float cos_theta, float shininess) const -> float;
155+
139156
Color SampleTexture(const Texture &texture, const Vector2f &uv) const;
140157
Color ClampColor(const Color color) const;
141158
};

src/shader.cpp

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,56 @@
11
#include "shader.hpp"
22

3+
#include <algorithm>
4+
#include <cmath>
5+
#include <mutex>
6+
#include <shared_mutex>
7+
38
namespace simple_renderer {
49

10+
Shader::Shader(const Shader& shader) {
11+
std::shared_lock lock(shader.specular_cache_mutex_);
12+
uniformbuffer_ = shader.uniformbuffer_;
13+
sharedDataInShader_ = shader.sharedDataInShader_;
14+
vertex_uniform_cache_ = shader.vertex_uniform_cache_;
15+
fragment_uniform_cache_ = shader.fragment_uniform_cache_;
16+
specular_lut_cache_ = shader.specular_lut_cache_;
17+
}
18+
19+
Shader::Shader(Shader&& shader) noexcept {
20+
std::unique_lock lock(shader.specular_cache_mutex_);
21+
uniformbuffer_ = std::move(shader.uniformbuffer_);
22+
sharedDataInShader_ = shader.sharedDataInShader_;
23+
vertex_uniform_cache_ = shader.vertex_uniform_cache_;
24+
fragment_uniform_cache_ = shader.fragment_uniform_cache_;
25+
specular_lut_cache_ = std::move(shader.specular_lut_cache_);
26+
}
27+
28+
auto Shader::operator=(const Shader& shader) -> Shader& {
29+
if (this == &shader) {
30+
return *this;
31+
}
32+
std::shared_lock lock(shader.specular_cache_mutex_);
33+
uniformbuffer_ = shader.uniformbuffer_;
34+
sharedDataInShader_ = shader.sharedDataInShader_;
35+
vertex_uniform_cache_ = shader.vertex_uniform_cache_;
36+
fragment_uniform_cache_ = shader.fragment_uniform_cache_;
37+
specular_lut_cache_ = shader.specular_lut_cache_;
38+
return *this;
39+
}
40+
41+
auto Shader::operator=(Shader&& shader) noexcept -> Shader& {
42+
if (this == &shader) {
43+
return *this;
44+
}
45+
std::unique_lock lock(shader.specular_cache_mutex_);
46+
uniformbuffer_ = std::move(shader.uniformbuffer_);
47+
sharedDataInShader_ = shader.sharedDataInShader_;
48+
vertex_uniform_cache_ = shader.vertex_uniform_cache_;
49+
fragment_uniform_cache_ = shader.fragment_uniform_cache_;
50+
specular_lut_cache_ = std::move(shader.specular_lut_cache_);
51+
return *this;
52+
}
53+
554
Vertex Shader::VertexShader(const Vertex& vertex) {
655
const bool cache_ready = vertex_uniform_cache_.derived_valid;
756

@@ -159,6 +208,56 @@ void Shader::PrepareFragmentUniformCache() {
159208
}
160209
}
161210

211+
auto Shader::BuildSpecularLUT(float shininess) const -> SpecularLUT {
212+
SpecularLUT lut;
213+
if (shininess <= 0.0f) {
214+
lut.values.fill(1.0f);
215+
return lut;
216+
}
217+
218+
for (size_t i = 0; i < kSpecularLutResolution; ++i) {
219+
float cos_theta = static_cast<float>(i) /
220+
static_cast<float>(kSpecularLutResolution - 1);
221+
lut.values[i] = cos_theta <= 0.0f ? 0.0f : std::pow(cos_theta, shininess);
222+
}
223+
return lut;
224+
}
225+
226+
auto Shader::GetSpecularLUT(float shininess) const -> const SpecularLUT& {
227+
uint32_t key = std::bit_cast<uint32_t>(shininess);
228+
{
229+
std::shared_lock lock(specular_cache_mutex_);
230+
auto it = specular_lut_cache_.find(key);
231+
if (it != specular_lut_cache_.end()) {
232+
return it->second;
233+
}
234+
}
235+
236+
SpecularLUT lut = BuildSpecularLUT(shininess);
237+
std::unique_lock lock(specular_cache_mutex_);
238+
auto [it, inserted] = specular_lut_cache_.emplace(key, std::move(lut));
239+
return it->second;
240+
}
241+
242+
auto Shader::EvaluateSpecular(float cos_theta, float shininess) const -> float {
243+
cos_theta = std::clamp(cos_theta, 0.0f, 1.0f);
244+
if (shininess <= 0.0f) {
245+
return 1.0f;
246+
}
247+
if (cos_theta <= 0.0f) {
248+
return 0.0f;
249+
}
250+
251+
const auto& lut = GetSpecularLUT(shininess);
252+
float scaled = cos_theta * static_cast<float>(kSpecularLutResolution - 1);
253+
size_t index = static_cast<size_t>(scaled);
254+
float frac = scaled - static_cast<float>(index);
255+
256+
const float v0 = lut.values[index];
257+
const float v1 = lut.values[std::min(index + 1, kSpecularLutResolution - 1)];
258+
return v0 + (v1 - v0) * frac;
259+
}
260+
162261
Color Shader::FragmentShader(const Fragment& fragment) const {
163262
// interpolate Normal, Color and UV
164263
Color interpolateColor = fragment.color;
@@ -202,8 +301,8 @@ Color Shader::FragmentShader(const Fragment& fragment) const {
202301
}
203302

204303
Vector3f halfVector = glm::normalize(light_dir + view_dir);
205-
float spec = std::pow(std::max(glm::dot(normal, halfVector), 0.0f),
206-
material.shininess);
304+
float cos_theta = std::max(glm::dot(normal, halfVector), 0.0f);
305+
float spec = EvaluateSpecular(cos_theta, material.shininess);
207306
if (material.has_specular_texture) {
208307
Color texture_color = SampleTexture(material.specular_texture, uv);
209308
specular_color = texture_color * spec;

0 commit comments

Comments
 (0)