工作流 /
3D 模型 /
Patina:材质提取

Patina:材质提取
Patina: Material Extract
用 Patina 从一张表面照片里提取出可无缝平铺的 PBR 材质。提示词说明要提取哪种表面、保留什么结构,输出的基础贴图与 basecolor、normal、roughness、metalness、height 五张图逐像素对齐。适合照片本身就已经包含目标材质的场景:砖墙、铺装、灰泥、织物。
3D 模型
10 个节点 1 个输入 ComfyUI ≥ 0.43.1 ComfyUI 15 KB
节点构成
SaveImage
× 6
SaveText
× 1
MarkdownNote
× 1
LoadImage
× 1
PatinaMaterialExtractNode
× 1
官方标注的模型
Patina
工作流 JSON
和上面「下载工作流 JSON」是同一份文件。
1{
2 "id": "65921f96-39e0-48c0-90f0-48a3907755e3",
3 "revision": 0,
4 "last_node_id": 31,
5 "last_link_id": 24,
6 "nodes": [
7 {
8 "id": 21,
9 "type": "SaveImage",
10 "pos": [
11 913.024172616747,
12 8.987101490822187
13 ],
14 "size": [
15 373.6485367550731,
16 421.999244495615
17 ],
18 "flags": [],
19 "order": 4,
20 "mode": 0,
21 "inputs": [
22 {
23 "name": "images",
24 "type": "IMAGE",
25 "link": 19
26 }
27 ],
28 "outputs": [
29 {
30 "name": "images",
31 "type": "IMAGE",
32 "links": null
33 }
34 ],
35 "title": "basecolor",
36 "properties": {
37 "Node name for S&R": "SaveImage"
38 },
39 "widgets_values": [
40 "patina/%year%%month%%day%-%hour%%minute%_basecolor"
41 ],
42 "widgets_values_named": {
43 "filename_prefix": "patina/%year%%month%%day%-%hour%%minute%_basecolor"
44 }
45 },
46 {
47 "id": 22,
48 "type": "SaveImage",
49 "pos": [
50 69.7270991066008,
51 8.987101490822187
52 ],
53 "size": [
54 373.6485367550731,
55 421.999244495615
56 ],
57 "flags": [],
58 "order": 5,
59 "mode": 0,
60 "inputs": [
61 {
62 "name": "images",
63 "type": "IMAGE",
64 "link": 20
65 }
66 ],
67 "outputs": [
68 {
69 "name": "images",
70 "type": "IMAGE",
71 "links": null
72 }
73 ],
74 "title": "normal",
75 "properties": {
76 "Node name for S&R": "SaveImage"
77 },
78 "widgets_values": [
79 "patina/%year%%month%%day%-%hour%%minute%_normal"
80 ],
81 "widgets_values_named": {
82 "filename_prefix": "patina/%year%%month%%day%-%hour%%minute%_normal"
83 }
84 },
85 {
86 "id": 23,
87 "type": "SaveImage",
88 "pos": [
89 491.3756358616739,
90 8.987101490822187
91 ],
92 "size": [
93 373.6485367550731,
94 421.999244495615
95 ],
96 "flags": [],
97 "order": 3,
98 "mode": 0,
99 "inputs": [
100 {
101 "name": "images",
102 "type": "IMAGE",
103 "link": 18
104 }
105 ],
106 "outputs": [
107 {
108 "name": "images",
109 "type": "IMAGE",
110 "links": null
111 }
112 ],
113 "title": "texture",
114 "properties": {
115 "Node name for S&R": "SaveImage"
116 },
117 "widgets_values": [
118 "patina/%year%%month%%day%-%hour%%minute%_texture"
119 ],
120 "widgets_values_named": {
121 "filename_prefix": "patina/%year%%month%%day%-%hour%%minute%_texture"
122 }
123 },
124 {
125 "id": 24,
126 "type": "SaveImage",
127 "pos": [
128 913.024172616747,
129 508.9863459864372
130 ],
131 "size": [
132 373.6485367550731,
133 421.999244495615
134 ],
135 "flags": [],
136 "order": 6,
137 "mode": 0,
138 "inputs": [
139 {
140 "name": "images",
141 "type": "IMAGE",
142 "link": 21
143 }
144 ],
145 "outputs": [
146 {
147 "name": "images",
148 "type": "IMAGE",
149 "links": []
150 }
151 ],
152 "title": "roughness",
153 "properties": {
154 "Node name for S&R": "SaveImage"
155 },
156 "widgets_values": [
157 "patina/%year%%month%%day%-%hour%%minute%_roughness"
158 ],
159 "widgets_values_named": {
160 "filename_prefix": "patina/%year%%month%%day%-%hour%%minute%_roughness"
161 }
162 },
163 {
164 "id": 25,
165 "type": "SaveImage",
166 "pos": [
167 69.7270991066008,
168 508.9863459864372
169 ],
170 "size": [
171 373.6485367550731,
172 421.999244495615
173 ],
174 "flags": [],
175 "order": 7,
176 "mode": 0,
177 "inputs": [
178 {
179 "name": "images",
180 "type": "IMAGE",
181 "link": 22
182 }
183 ],
184 "outputs": [
185 {
186 "name": "images",
187 "type": "IMAGE",
188 "links": []
189 }
190 ],
191 "title": "metalness",
192 "properties": {
193 "Node name for S&R": "SaveImage"
194 },
195 "widgets_values": [
196 "patina/%year%%month%%day%-%hour%%minute%_metalness"
197 ],
198 "widgets_values_named": {
199 "filename_prefix": "patina/%year%%month%%day%-%hour%%minute%_metalness"
200 }
201 },
202 {
203 "id": 26,
204 "type": "SaveImage",
205 "pos": [
206 491.3756358616739,
207 508.9863459864372
208 ],
209 "size": [
210 373.6485367550731,
211 421.999244495615
212 ],
213 "flags": [],
214 "order": 8,
215 "mode": 0,
216 "inputs": [
217 {
218 "name": "images",
219 "type": "IMAGE",
220 "link": 23
221 }
222 ],
223 "outputs": [
224 {
225 "name": "images",
226 "type": "IMAGE",
227 "links": []
228 }
229 ],
230 "title": "height",
231 "properties": {
232 "Node name for S&R": "SaveImage"
233 },
234 "widgets_values": [
235 "patina/%year%%month%%day%-%hour%%minute%_height"
236 ],
237 "widgets_values_named": {
238 "filename_prefix": "patina/%year%%month%%day%-%hour%%minute%_height"
239 }
240 },
241 {
242 "id": 27,
243 "type": "SaveText",
244 "pos": [
245 1344.4437364341845,
246 10.211257943315161
247 ],
248 "size": [
249 402.051097237096,
250 411.75603311460543
251 ],
252 "flags": [],
253 "order": 9,
254 "mode": 0,
255 "inputs": [
256 {
257 "name": "text",
258 "type": "STRING",
259 "link": 24
260 }
261 ],
262 "outputs": [
263 {
264 "name": "text",
265 "type": "STRING",
266 "links": null
267 }
268 ],
269 "title": "expanded prompt",
270 "properties": {
271 "Node name for S&R": "SaveText"
272 },
273 "widgets_values": [
274 "patina/%year%%month%%day%-%hour%%minute%_expanded_prompt",
275 "txt"
276 ],
277 "widgets_values_named": {
278 "filename_prefix": "patina/%year%%month%%day%-%hour%%minute%_expanded_prompt",
279 "format": "txt"
280 }
281 },
282 {
283 "id": 29,
284 "type": "MarkdownNote",
285 "pos": [
286 -1557.265453790667,
287 8.902110424501338
288 ],
289 "size": [
290 587.2726037200163,
291 782.0166239750356
292 ],
293 "flags": [],
294 "order": 0,
295 "mode": 0,
296 "inputs": [],
297 "outputs": [],
298 "properties": [],
299 "widgets_values": [
300 "# Patina Material Extract\n\n**Photo -> seamless, tiling PBR material, extracted.** Point it at a photo that already contains the material you want\nand the prompt says which surface to pull out and what structure to keep. The base texture and the basecolor / normal /\nroughness / metalness / height maps all come from the same latent, so they line up pixel for pixel. Partner (API) node:\nit runs on Comfy's servers, needs an account, nothing renders locally.\n\n## Input Asset\n\n- [patina_chair_webbing.png](https://raw.githubusercontent.com/Comfy-Org/workflow_templates/refs/heads/main/input/patina_chair_webbing.png)\n\n## How to use it\n\n- **Load Image** holds the photo. It does not have to be a flat crop: the node re-synthesises the layout, so a product\n shot works as long as the material itself repeats - here the green and white webbing on a folding chair.\n- `prompt` names the surface to extract, not the scene: `chair webbing fabric` picks the woven straps and ignores the\n chair frame, the court and the paddle. Add the structure to keep (\"keep the weave and the strap edges\") when the\n pattern starts drifting.\n- `strength` (advanced, 0.6 here) sets how far the extracted texture may drift from the photo.\n- Works best on materials whose pattern repeats inside a few hundred pixels - webbing, brick, pavers, planks, plaster.\n Large one-off structures (a rock cliff, aerial terrain, a single object) come back as a repaint that will not tile.\n- For text-only material generation use the Patina Material text-to-material template; to convert a photo into maps\n **without** making it tile, the Patina PBR Maps node is the direct path.\n\n## Outputs - wire in this slot order\n\n`0 texture` . `1 basecolor` . `2 normal` . `3 roughness` . `4 metalness` . `5 height` . `6 expanded_prompt`\n\nEvery map can be switched off, but at least one has to stay on. Fewer maps = a cheaper, faster run.\n\n## Tips that actually matter\n\n- Keep the photo bigger than the output size where you can - the model has more real detail to extract from.\n- `num_inference_steps` tops out at 8; there is nothing to gain above that.\n- `tile_size` is in latent space: 128 is about 1024 px, 64 about 512 px. `tiling_mode` = both / horizontal / vertical.\n- `prompt_expansion` is **off by default on purpose**: the LLM rewrite reframes the prompt as a photo and tends to\n wash out the metalness map.\n- `upscale_factor` 2 or 4 upscales the **maps only** (SeedVR) - the base texture is never upscaled.\n- Dramatic lighting in the source carries into the basecolor: prefer flat, evenly lit photos."
301 ],
302 "widgets_values_named": {
303 "text": "# Patina Material Extract\n\n**Photo -> seamless, tiling PBR material, extracted.** Point it at a photo that already contains the material you want\nand the prompt says which surface to pull out and what structure to keep. The base texture and the basecolor / normal /\nroughness / metalness / height maps all come from the same latent, so they line up pixel for pixel. Partner (API) node:\nit runs on Comfy's servers, needs an account, nothing renders locally.\n\n## Input Asset\n\n- [patina_chair_webbing.png](https://raw.githubusercontent.com/Comfy-Org/workflow_templates/refs/heads/main/input/patina_chair_webbing.png)\n\n## How to use it\n\n- **Load Image** holds the photo. It does not have to be a flat crop: the node re-synthesises the layout, so a product\n shot works as long as the material itself repeats - here the green and white webbing on a folding chair.\n- `prompt` names the surface to extract, not the scene: `chair webbing fabric` picks the woven straps and ignores the\n chair frame, the court and the paddle. Add the structure to keep (\"keep the weave and the strap edges\") when the\n pattern starts drifting.\n- `strength` (advanced, 0.6 here) sets how far the extracted texture may drift from the photo.\n- Works best on materials whose pattern repeats inside a few hundred pixels - webbing, brick, pavers, planks, plaster.\n Large one-off structures (a rock cliff, aerial terrain, a single object) come back as a repaint that will not tile.\n- For text-only material generation use the Patina Material text-to-material template; to convert a photo into maps\n **without** making it tile, the Patina PBR Maps node is the direct path.\n\n## Outputs - wire in this slot order\n\n`0 texture` . `1 basecolor` . `2 normal` . `3 roughness` . `4 metalness` . `5 height` . `6 expanded_prompt`\n\nEvery map can be switched off, but at least one has to stay on. Fewer maps = a cheaper, faster run.\n\n## Tips that actually matter\n\n- Keep the photo bigger than the output size where you can - the model has more real detail to extract from.\n- `num_inference_steps` tops out at 8; there is nothing to gain above that.\n- `tile_size` is in latent space: 128 is about 1024 px, 64 about 512 px. `tiling_mode` = both / horizontal / vertical.\n- `prompt_expansion` is **off by default on purpose**: the LLM rewrite reframes the prompt as a photo and tends to\n wash out the metalness map.\n- `upscale_factor` 2 or 4 upscales the **maps only** (SeedVR) - the base texture is never upscaled.\n- Dramatic lighting in the source carries into the basecolor: prefer flat, evenly lit photos."
304 },
305 "color": "#222",
306 "bgcolor": "#000"
307 },
308 {
309 "id": 30,
310 "type": "LoadImage",
311 "pos": [
312 -916.0890251245451,
313 13.48273436379975
314 ],
315 "size": [
316 415.0581423487129,
317 432.7386056769318
318 ],
319 "flags": [],
320 "order": 1,
321 "mode": 0,
322 "inputs": [],
323 "outputs": [
324 {
325 "name": "IMAGE",
326 "type": "IMAGE",
327 "links": [
328 17
329 ]
330 },
331 {
332 "name": "MASK",
333 "type": "MASK",
334 "links": []
335 }
336 ],
337 "properties": {
338 "Node name for S&R": "LoadImage"
339 },
340 "widgets_values": [
341 "patina_chair_webbing.png",
342 "image"
343 ],
344 "widgets_values_named": {
345 "image": "patina_chair_webbing.png",
346 "upload": "image"
347 }
348 },
349 {
350 "id": 31,
351 "type": "PatinaMaterialExtractNode",
352 "pos": [
353 -459.0498746311147,
354 15.455660429013562
355 ],
356 "size": [
357 400,
358 592
359 ],
360 "flags": [],
361 "order": 2,
362 "mode": 0,
363 "inputs": [
364 {
365 "name": "image",
366 "type": "IMAGE",
367 "link": 17
368 }
369 ],
370 "outputs": [
371 {
372 "name": "texture",
373 "type": "IMAGE",
374 "links": [
375 18
376 ]
377 },
378 {
379 "name": "basecolor",
380 "type": "IMAGE",
381 "links": [
382 19
383 ]
384 },
385 {
386 "name": "normal",
387 "type": "IMAGE",
388 "links": [
389 20
390 ]
391 },
392 {
393 "name": "roughness",
394 "type": "IMAGE",
395 "links": [
396 21
397 ]
398 },
399 {
400 "name": "metalness",
401 "type": "IMAGE",
402 "links": [
403 22
404 ]
405 },
406 {
407 "name": "height",
408 "type": "IMAGE",
409 "links": [
410 23
411 ]
412 },
413 {
414 "name": "expanded_prompt",
415 "type": "STRING",
416 "links": [
417 24
418 ]
419 }
420 ],
421 "properties": {
422 "Node name for S&R": "PatinaMaterialExtractNode"
423 },
424 "widgets_values": [
425 "chair webbing fabric",
426 "1:1 (1024x1024)",
427 true,
428 true,
429 true,
430 true,
431 true,
432 0,
433 1766525070,
434 "randomize",
435 0.6,
436 "both",
437 8,
438 128,
439 64,
440 false,
441 false
442 ],
443 "widgets_values_named": {
444 "prompt": "chair webbing fabric",
445 "image_size": "1:1 (1024x1024)",
446 "basecolor": true,
447 "normal": true,
448 "roughness": true,
449 "metalness": true,
450 "height": true,
451 "upscale_factor": 0,
452 "seed": 1766525070,
453 "control_after_generate": "randomize",
454 "strength": 0.6,
455 "tiling_mode": "both",
456 "num_inference_steps": 8,
457 "tile_size": 128,
458 "tile_stride": 64,
459 "prompt_expansion": false,
460 "safety_checker": false
461 },
462 "color": "#432",
463 "bgcolor": "#653"
464 }
465 ],
466 "links": [
467 [
468 17,
469 30,
470 0,
471 31,
472 0,
473 "IMAGE"
474 ],
475 [
476 18,
477 31,
478 0,
479 23,
480 0,
481 "IMAGE"
482 ],
483 [
484 19,
485 31,
486 1,
487 21,
488 0,
489 "IMAGE"
490 ],
491 [
492 20,
493 31,
494 2,
495 22,
496 0,
497 "IMAGE"
498 ],
499 [
500 21,
501 31,
502 3,
503 24,
504 0,
505 "IMAGE"
506 ],
507 [
508 22,
509 31,
510 4,
511 25,
512 0,
513 "IMAGE"
514 ],
515 [
516 23,
517 31,
518 5,
519 26,
520 0,
521 "IMAGE"
522 ],
523 [
524 24,
525 31,
526 6,
527 27,
528 0,
529 "STRING"
530 ]
531 ],
532 "groups": [],
533 "config": [],
534 "extra": {
535 "ds": {
536 "scale": 0.3235581649241681,
537 "offset": [
538 2149.4679942405587,
539 819.4744029556668
540 ]
541 },
542 "frontendVersion": "1.57.4"
543 },
544 "version": 0.4
545}