载入中...
搜索中...
未找到
PbrMaterial.cpp
浏览该文件的文档.
6
7#include "image/ImageData.h"
8
9#include <algorithm>
10#include <cmath>
11#include <utility>
12
13namespace eve::procgen {
14
16
18 delete albedo;
19 delete normal;
20 delete roughness;
21 delete metallic;
22 delete height;
23 delete ao;
24 albedo = normal = roughness = metallic = height = ao = nullptr;
25}
26
31
35 descriptor.displayName = id;
36 registerPbrRecipe(std::move(descriptor), std::move(fn));
37}
38
40 const std::string id = descriptor.id;
41 recipes_[id] = Entry{std::move(fn), std::move(descriptor)};
42}
43
44bool PbrRecipeRegistry::has(const std::string &id) const {
45 return recipes_.find(id) != recipes_.end();
46}
47
48std::unique_ptr<PbrTextureSet> PbrRecipeRegistry::generate(const std::string &id, const Params &params,
49 std::string &error) const {
50 auto it = recipes_.find(id);
51 if (it == recipes_.end()) {
52 error = "unknown pbr recipe: " + id;
53 return {};
54 }
55 return it->second.fn(params, error);
56}
57
58const RecipeDescriptor *PbrRecipeRegistry::descriptor(const std::string &id) const {
59 const auto it = recipes_.find(id);
60 return it == recipes_.end() ? nullptr : &it->second.descriptor;
61}
62
63bool PbrRecipeRegistry::applyDefaults(const std::string &id, Params &params) const {
64 const RecipeDescriptor *schema = descriptor(id);
65 if (!schema) return false;
66 schema->applyDefaults(params);
67 return true;
68}
69
70std::vector<std::string> PbrRecipeRegistry::list() const {
71 std::vector<std::string> ids;
72 ids.reserve(recipes_.size());
73 for (const auto &kv : recipes_) ids.push_back(kv.first);
74 std::sort(ids.begin(), ids.end());
75 return ids;
76}
77
79 if (builtinsRegistered_) return;
80 for (const TextureRecipeDef &def : builtinTextureDefs()) {
81 const std::string id = "pbr." + def.id.substr(def.id.find('.') + 1);
83 descriptor.id = id;
84 descriptor.category = "Material";
85 descriptor.params.push_back(ParamDescriptor::floating("roughnessLow", "Low Roughness", def.pbr.roughnessLow, 0.f, 1.f, 0.01f));
86 descriptor.params.push_back(ParamDescriptor::floating("roughnessHigh", "High Roughness", def.pbr.roughnessHigh, 0.f, 1.f, 0.01f));
87 descriptor.params.push_back(ParamDescriptor::floating("metallic", "Metallic", def.pbr.metallic, 0.f, 1.f, 0.01f));
88 descriptor.params.push_back(ParamDescriptor::floating("normalStrength", "Normal Strength", def.pbr.normalStrength, 0.01f, 32.f, 0.05f));
89 descriptor.params.push_back(ParamDescriptor::floating("aoStrength", "AO Strength", def.pbr.aoStrength, 0.f, 5.f, 0.05f));
90 descriptor.params.push_back(ParamDescriptor::floating("heightStrength", "Height Strength", def.pbr.heightStrength, 0.f, 16.f, 0.05f));
91 registerPbrRecipe(std::move(descriptor), [def](const Params &params, std::string &error) {
92 return generatePbrSet(def, params, error);
93 });
94 }
97 builtinsRegistered_ = true;
98}
99
100std::unique_ptr<image::ImageData> grayscaleImage(const std::vector<float> &values, int w, int h) {
101 auto img = std::make_unique<image::ImageData>(w, h, "RGBA8");
102 auto *pixels = static_cast<uint8_t *>(img->getData());
103 for (int i = 0; i < w * h && i < int(values.size()); ++i) {
104 const uint8_t v = uint8_t(std::lround(std::clamp(values[size_t(i)], 0.f, 1.f) * 255.f));
105 const size_t o = size_t(i) * 4u;
106 pixels[o + 0] = pixels[o + 1] = pixels[o + 2] = v;
107 pixels[o + 3] = 255;
108 }
109 return img;
110}
111
112std::unique_ptr<PbrTextureSet> generatePbrSet(const TextureRecipeDef &def, const Params &params, std::string &error) {
113 const auto ctx = TextureGenContext::fromParams(params);
114 if (ctx.width > 4096 || ctx.height > 4096) {
115 error = "texture size too large (max 4096)";
116 return {};
117 }
118
119 // Recipe defaults, then per-call overrides.
120 PbrParams pbr = def.pbr;
121 pbr.roughnessLow = std::clamp(params.getFloat("roughnessLow", pbr.roughnessLow), 0.f, 1.f);
122 pbr.roughnessHigh = std::clamp(params.getFloat("roughnessHigh", pbr.roughnessHigh), 0.f, 1.f);
123 pbr.metallic = std::clamp(params.getFloat("metallic", pbr.metallic), 0.f, 1.f);
124 pbr.normalStrength = std::max(0.01f, params.getFloat("normalStrength", pbr.normalStrength));
125 pbr.aoStrength = std::clamp(params.getFloat("aoStrength", pbr.aoStrength), 0.f, 5.f);
126 pbr.heightStrength = std::max(0.f, params.getFloat("heightStrength", pbr.heightStrength));
127
128 std::vector<float> height;
129 fillHeightField(ctx, def.height, height);
130 const int w = ctx.width;
131 const int h = ctx.height;
132
133 auto set = std::make_unique<PbrTextureSet>();
134 set->albedo = new image::ImageData(w, h, "RGBA8");
135 paintHeightToImage(*set->albedo, height, w, h, def.albedo, ctx.colors, ctx.pixelSize);
136 set->normal = heightToNormalImage(height, w, h, pbr.normalStrength, ctx.seamless).release();
137
138 std::vector<float> rough(size_t(w * h));
139 std::vector<float> heightMap(size_t(w * h));
140 std::vector<float> ao(size_t(w * h));
141 const int r = std::max(1, std::min(w, h) / 64); // cavity sampling radius
142 for (int y = 0; y < h; ++y) {
143 for (int x = 0; x < w; ++x) {
144 const size_t i = size_t(y) * size_t(w) + size_t(x);
145 const float hh = height[i];
146 rough[size_t(i)] =
147 pbr.roughnessLow + (pbr.roughnessHigh - pbr.roughnessLow) * hh;
148 heightMap[i] = std::clamp(0.5f + (hh - 0.5f) * pbr.heightStrength, 0.f, 1.f);
149
150 // Cavity AO: darker where a pixel sits below its neighbourhood mean.
151 float sum = 0.f;
152 int cnt = 0;
153 for (int dy = -r; dy <= r; ++dy) {
154 for (int dx = -r; dx <= r; ++dx) {
155 int xx = x + dx, yy = y + dy;
156 if (ctx.seamless) {
157 xx = ((xx % w) + w) % w;
158 yy = ((yy % h) + h) % h;
159 } else {
160 if (xx < 0 || yy < 0 || xx >= w || yy >= h) continue;
161 }
162 sum += height[size_t(yy) * size_t(w) + size_t(xx)];
163 ++cnt;
164 }
165 }
166 const float local = cnt > 0 ? sum / float(cnt) : hh;
167 ao[i] = std::clamp(1.f - pbr.aoStrength * std::max(0.f, local - hh), 0.f, 1.f);
168 }
169 }
170 set->roughness = grayscaleImage(rough, w, h).release();
171 set->metallic = grayscaleImage(std::vector<float>(size_t(w * h), pbr.metallic), w, h).release();
172 set->height = grayscaleImage(heightMap, w, h).release();
173 set->ao = grayscaleImage(ao, w, h).release();
174 return set;
175}
176
177} // namespace eve::procgen
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
std::string descriptor
std::map< std::string, Var > values
glm::uvec4 ids
double r
float v
int h
std::uint32_t height
std::string local
graphics::PbrSurface pbr
std::string error
Definition Package.cpp:60
std::string id
Definition PlayHost.cpp:108
uint8_t * pixels
float dy
float dx
Represents raw pixel data.
Definition ImageData.h:38
Owning, typed generation parameters.
Definition Params.h:27
EVENGINE_API_DOMAINS public API.
Definition PbrMaterial.h:55
const RecipeDescriptor * descriptor(const std::string &id) const
Look up recipe metadata.
static PbrRecipeRegistry & instance()
Access the process-wide PBR registry.
bool applyDefaults(const std::string &id, Params &params) const
Fill missing values from metadata.
std::vector< std::string > list() const
List recipe ids.
std::unique_ptr< PbrTextureSet > generate(const std::string &id, const Params &params, std::string &error) const
Generates a PBR map set and transfers its unique ownership to the caller.
void registerPbrBuiltins()
Register engine-provided PBR recipes once.
void registerPbrRecipe(const std::string &id, PbrRecipeFn fn)
Register a recipe without metadata.
bool has(const std::string &id) const
Test whether a recipe exists.
std::vector< ParamSpec > params
void registerFloorPbrRecipes(PbrRecipeRegistry &registry)
Register pbr.floor.wood and pbr.floor.tile with schema metadata.
void registerCablePbrRecipes(PbrRecipeRegistry &registry)
Register pbr.cable.steel / pbr.chain.iron / pbr.rope.hemp.
std::unique_ptr< PbrTextureSet > generatePbrSet(const TextureRecipeDef &def, const Params &params, std::string &error)
Builds a full PBR set from a texture definition and returns unique ownership.
RecipeDescriptor makeTextureRecipeDescriptor(const TextureRecipeDef &definition)
Build editable metadata for a texture definition.
std::unique_ptr< image::ImageData > grayscaleImage(const std::vector< float > &values, int w, int h)
Builds a grayscale RGBA8 image and returns unique ownership to the caller.
const std::vector< TextureRecipeDef > & builtinTextureDefs()
All built-in texture definitions (albedo + PBR). Populated lazily.
void paintHeightToImage(image::ImageData &img, const std::vector< float > &height, int w, int h, const ColorRamp &ramp, int bands, int pixelSize)
Paint height to image.
void fillHeightField(const TextureGenContext &ctx, const std::function< float(float, float, const NoiseField &)> &fn, std::vector< float > &height)
Sample a height function over the full [w×h] grid into height (clamped to [0,1]).
std::unique_ptr< image::ImageData > heightToNormalImage(const std::vector< float > &height, int w, int h, float strength, bool seamless)
Builds a normal-map image and transfers its unique ownership to the caller.
std::function< std::unique_ptr< PbrTextureSet >(const Params &params, std::string &error)> PbrRecipeFn
Recipe returns a newly owned full PBR set, or null on failure.
Definition PbrMaterial.h:52
SettlementPipeline::Stage fn
static ParamDescriptor floating(std::string key, std::string label, float defaultValue, float minimum, float maximum, float step)
Construct a bounded floating-point descriptor.
PBR surface defaults for a generated material. Every map in a PBR set is derived from the same displa...
~PbrTextureSet()
Releases all generated maps when the owning set is destroyed.
void destroy()
Deletes all maps and nulls the pointers (defined in PbrMaterial.cpp).
image::ImageData * normal
Definition PbrMaterial.h:29
image::ImageData * height
Definition PbrMaterial.h:32
image::ImageData * ao
Definition PbrMaterial.h:33
image::ImageData * albedo
Definition PbrMaterial.h:28
image::ImageData * roughness
Definition PbrMaterial.h:30
image::ImageData * metallic
Definition PbrMaterial.h:31
Complete metadata and parameter schema shared by every procgen recipe family.
Definition ParamSchema.h:52
void applyDefaults(Params &params) const
Fill missing Params values from this schema.
static TextureGenContext fromParams(const Params &params)
From params.
A procedural texture is fully described by an albedo ramp + a displacement field + PBR knobs....
std::function< float(float, float, const NoiseField &)> height
Float.