载入中...
搜索中...
未找到
PbrMaterial.cpp
浏览该文件的文档.
4
5#include "image/ImageData.h"
6
7#include <algorithm>
8#include <cmath>
9
10namespace eve::procgen {
11
16
18 recipes_[id] = std::move(fn);
19}
20
21bool PbrRecipeRegistry::has(const std::string &id) const {
22 return recipes_.find(id) != recipes_.end();
23}
24
25PbrTextureSet *PbrRecipeRegistry::generate(const std::string &id, const Params &params,
26 std::string &error) const {
27 auto it = recipes_.find(id);
28 if (it == recipes_.end()) {
29 error = "unknown pbr recipe: " + id;
30 return nullptr;
31 }
32 return it->second(params, error);
33}
34
35std::vector<std::string> PbrRecipeRegistry::list() const {
36 std::vector<std::string> ids;
37 ids.reserve(recipes_.size());
38 for (const auto &kv : recipes_) ids.push_back(kv.first);
39 std::sort(ids.begin(), ids.end());
40 return ids;
41}
42
44 if (builtinsRegistered_) return;
45 for (const TextureRecipeDef &def : builtinTextureDefs()) {
46 const std::string id = "pbr." + def.id.substr(def.id.find('.') + 1);
47 registerPbrRecipe(id, [def](const Params &params, std::string &error) {
48 return generatePbrSet(def, params, error);
49 });
50 }
51 builtinsRegistered_ = true;
52}
53
54image::ImageData *grayscaleImage(const std::vector<float> &values, int w, int h) {
55 auto *img = new image::ImageData(w, h, "RGBA8");
56 auto *pixels = static_cast<uint8_t *>(img->getData());
57 for (int i = 0; i < w * h && i < int(values.size()); ++i) {
58 const uint8_t v = uint8_t(std::lround(std::clamp(values[size_t(i)], 0.f, 1.f) * 255.f));
59 const size_t o = size_t(i) * 4u;
60 pixels[o + 0] = pixels[o + 1] = pixels[o + 2] = v;
61 pixels[o + 3] = 255;
62 }
63 return img;
64}
65
67 std::string &error) {
68 const auto ctx = TextureGenContext::fromParams(params);
69 if (ctx.width > 4096 || ctx.height > 4096) {
70 error = "texture size too large (max 4096)";
71 return nullptr;
72 }
73
74 // Recipe defaults, then per-call overrides.
75 PbrParams pbr = def.pbr;
76 pbr.roughnessLow = std::clamp(params.getFloat("roughnessLow", pbr.roughnessLow), 0.f, 1.f);
77 pbr.roughnessHigh = std::clamp(params.getFloat("roughnessHigh", pbr.roughnessHigh), 0.f, 1.f);
78 pbr.metallic = std::clamp(params.getFloat("metallic", pbr.metallic), 0.f, 1.f);
79 pbr.normalStrength = std::max(0.01f, params.getFloat("normalStrength", pbr.normalStrength));
80 pbr.aoStrength = std::clamp(params.getFloat("aoStrength", pbr.aoStrength), 0.f, 5.f);
81 pbr.heightStrength = std::max(0.f, params.getFloat("heightStrength", pbr.heightStrength));
82
83 std::vector<float> height;
84 fillHeightField(ctx, def.height, height);
85 const int w = ctx.width;
86 const int h = ctx.height;
87
88 auto *set = new PbrTextureSet();
89 set->albedo = new image::ImageData(w, h, "RGBA8");
90 paintHeightToImage(*set->albedo, height, w, h, def.albedo, ctx.colors, ctx.pixelSize);
91 set->normal = heightToNormalImage(height, w, h, pbr.normalStrength, ctx.seamless);
92
93 std::vector<float> rough(size_t(w * h));
94 std::vector<float> heightMap(size_t(w * h));
95 std::vector<float> ao(size_t(w * h));
96 const int r = std::max(1, std::min(w, h) / 64); // cavity sampling radius
97 for (int y = 0; y < h; ++y) {
98 for (int x = 0; x < w; ++x) {
99 const size_t i = size_t(y) * size_t(w) + size_t(x);
100 const float hh = height[i];
101 rough[size_t(i)] =
102 pbr.roughnessLow + (pbr.roughnessHigh - pbr.roughnessLow) * hh;
103 heightMap[i] = std::clamp(0.5f + (hh - 0.5f) * pbr.heightStrength, 0.f, 1.f);
104
105 // Cavity AO: darker where a pixel sits below its neighbourhood mean.
106 float sum = 0.f;
107 int cnt = 0;
108 for (int dy = -r; dy <= r; ++dy) {
109 for (int dx = -r; dx <= r; ++dx) {
110 int xx = x + dx, yy = y + dy;
111 if (ctx.seamless) {
112 xx = ((xx % w) + w) % w;
113 yy = ((yy % h) + h) % h;
114 } else {
115 if (xx < 0 || yy < 0 || xx >= w || yy >= h) continue;
116 }
117 sum += height[size_t(yy) * size_t(w) + size_t(xx)];
118 ++cnt;
119 }
120 }
121 const float local = cnt > 0 ? sum / float(cnt) : hh;
122 ao[i] = std::clamp(1.f - pbr.aoStrength * std::max(0.f, local - hh), 0.f, 1.f);
123 }
124 }
125 set->roughness = grayscaleImage(rough, w, h);
126 set->metallic = grayscaleImage(std::vector<float>(size_t(w * h), pbr.metallic), w, h);
127 set->height = grayscaleImage(heightMap, w, h);
128 set->ao = grayscaleImage(ao, w, h);
129 return set;
130}
131
132} // namespace eve::procgen
std::map< std::string, Var > values
std::string id
int y
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
int x
Definition Grass.cpp:135
int h
int w
std::string error
SettlementPipeline::Stage fn
int v
Represents raw pixel data.
Definition ImageData.h:26
Generation parameters. Algorithm-specific keys live in values as strings (no overloads; typed setters...
Definition Params.h:13
float getFloat(const std::string &key, float defaultValue) const
Definition Params.cpp:32
static PbrRecipeRegistry & instance()
std::vector< std::string > list() const
PbrTextureSet * generate(const std::string &id, const Params &params, std::string &error) const
void registerPbrRecipe(const std::string &id, PbrRecipeFn fn)
bool has(const std::string &id) const
std::function< PbrTextureSet *(const Params &params, std::string &error)> PbrRecipeFn
Recipe returns a full PBR set (caller owns), or nullptr on failure.
Definition PbrMaterial.h:44
const std::vector< TextureRecipeDef > & builtinTextureDefs()
All built-in texture definitions (albedo + PBR). Populated lazily.
image::ImageData * grayscaleImage(const std::vector< float > &values, int w, int h)
Build a grayscale RGBA8 image from per-pixel values in [0,1]. Caller owns.
void paintHeightToImage(image::ImageData &img, const std::vector< float > &height, int w, int h, const ColorRamp &ramp, int bands, int pixelSize)
image::ImageData * heightToNormalImage(const std::vector< float > &height, int w, int h, float strength, bool seamless)
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]).
PbrTextureSet * generatePbrSet(const TextureRecipeDef &def, const Params &params, std::string &error)
Build a full PBR set from a texture definition. Caller owns the result.
PBR surface defaults for a generated material. Every map in a PBR set is derived from the same displa...
Full metallic-roughness PBR map set. All maps are caller-owned; call destroy() (or delete each pointe...
Definition PbrMaterial.h:24
static TextureGenContext fromParams(const Params &params)
A procedural texture is fully described by an albedo ramp + a displacement field + PBR knobs....
std::function< float(float, float, const NoiseField &)> height