Day 38 - ⚡️ Quick - Texel Density - Targets and Validation

General / 23 June 2026

Part 2: what targets to aim for, how LODs change the picture, and how to automate the check.


In Practice

There are no universal targets, but the numbers that come up consistently across the industry follow camera distance:

Each step roughly halves as the camera gets further from the asset - which tracks with how mip levels work. A first-person weapon can fill 40% of the screen; a crate in a third-person game rarely does.

These are per-category baselines, not absolutes. A AAA first-person game might push hero weapons to 20+ px/cm while background architecture sits at 5. An open-world title might cap everything at 5 purely for memory budget, regardless of perspective. The right number is the one that fits your project's constraints - these are just a starting point for setting your validation range.


Validation

The formula from Part 1 is most useful when you run it across an entire asset set rather than checking meshes one at a time. The goal is a pipeline pass that computes TD for every asset and flags anything outside your target range.

In practice this means defining a minimum and maximum threshold per asset category - for example, props in a third-person game might target 4.5-6.0 px/cm, with anything below flagged as too low-resolution and anything above flagged as wasting memory budget. The script compares each result against those bounds and outputs a report or fails the build step.

This catches two common problems: artists who unwrap efficiently but use the wrong texture resolution, and assets that were authored for a different project or camera distance and pulled in without a rescale. A single number per asset makes both of those immediately visible.


Level of Detail

TD requirements relax as LOD level increases, because the asset is further from the camera and occupies fewer screen pixels. The same logic that sets your L0 target also tells you how aggressively you can drop at each subsequent level - if your L0 target is 5.12 px/cm and each LOD step roughly doubles the draw distance, you can halve the TD at each step. L1 at 2.56 px/cm, L2 at 1.28 px/cm, and so on, until the geometry is simple enough that the texture is no longer the limiting factor.

In practice this means your validation thresholds should be per-LOD, not per-asset. Flagging an L2 mesh for low texel density against an L0 threshold will produce false positives on every asset. Set a target range for each LOD level separately.

For very distant LODs, a reprojection or distant-LOD bake is often more practical than trying to maintain a coherent UV layout - at that distance the asset is gestural anyway, and a flat projection onto a small atlas tile is faster to author and cheaper to render than a properly unwrapped mesh.

© 2026 Stefan Groenewoud - All views are my own, not those of my employer.

Day 37 - ⚡️ Quick - Texel Density Calculations

General / 22 June 2026

How to measure whether your assets are consistent - and catch the ones that aren't before they ship.


What Is Texel Density?

Texel density measures how many texels map to one unit of real-world surface area. It's the consistency metric that ensures assets read correctly relative to each other at the same distance - if a wall has 10 texels per centimeter and a crate next to it has 2, the crate will look blurry by comparison even at the same resolution.


Why does it matter?

Higher texel density doesn't automatically mean better quality. There's a ceiling set by how many pixels the asset actually occupies on screen. A cup in a third-person game might only cover a small region of the viewport regardless of how dense its texture is - forcing a higher mip doesn't recover detail, it just introduces aliasing. The texture is already resolving finer than the screen can show.

The budget argument compounds this. Standardizing at an unnecessarily high texel density bloats texture memory and file sizes across every asset in the project. Large textures that stay resident in VRAM have a direct cost on GPU performance. Getting texel density right is about matching the resolution of your textures to what the game can actually use - not maximizing it.

source: renderhub.com


Formula

def CalculateTexelDensity(inMesh, inWidth, inHeight):
    return sqrt(
        sum(GetUVArea(inMesh) * (inHeight * inWidth)) /
        sum(GetSurfaceAreas(inMesh)))


Where:

  • GetUVArea - UV area of the mesh in UV space (0-1 range)
  • inWidth / inHeight - texture resolution
  • GetSurfaceAreas - world-space surface area of the mesh


© 2026 Stefan Groenewoud - All views are my own, not those of my employer.

Day 36 - 🔬 Deep dive - LOD Pipelines - The Math Behind the Decisions

General / 19 June 2026

One measurement decides when a mesh swaps to a cheaper version. Get it wrong and assets pop or waste budget. Here's how to size an object from every angle and turn that into a draw distance.

Welcome back to Part 2 of our discussion on level of detail meshes. This one builds directly on the first post, so it's a little shorter. In Part 1 we calculated the switch distance from a single number: the diagonal of the object. That works for roughly cubic assets, but it falls apart the moment something is thin or flat, because a player in a game sees the mesh from every direction, not just along its longest line.


Why one angle isn't enough

The diagonal is dominated by the largest dimensions. For a flat asset, a wall panel, a sign, a leaf, the diagonal makes the object look bigger than it ever appears side-on, and the LOD lingers at full detail longer than it should. Measure only the thin face and you get the opposite problem: the asset drops to a cheaper mesh while it's still filling a chunk of the screen.

The fix is to stop trusting one number. I measure the object from a few representative directions, full frontal, full side, full top, plus the 3D diagonal, and feed an aggregate of those into the distance calculation. Thin and flat objects get a fairer reading because the orientation the artist happens to place them in no longer dominates the result.


Measuring from every side

Each face measurement is just a 2D diagonal of two dimensions; the overall diagonal is the 3D version. Pull the width, height and depth from the asset's bounding box, and wrap each one in Abs() so a bounding-box corner with negative coordinates can't flip a sign on you (thanks to the reader who flagged that on Part 1).

Correction to Parts 1 and 2: the earlier posts computed these with a cube root and reported the diagonal as 0.387 m. That was my mistake. A diagonal is the square root of the sum of squares, no matter how many dimensions you combine, that's just the Pythagorean theorem extended to 3D. Cube-rooting squared metres doesn't even return a length (the units come out as m^⅔). The numbers below are the corrected versions.

pseudo code
diagonal = √(0.075² + 0.157² + 0.167²) = 0.241 m
front    = √(0.075² + 0.157²)          = 0.174 m
side     = √(0.157² + 0.167²)          = 0.229 m
top      = √(0.075² + 0.167²)          = 0.183 m
mean = (0.241 + 0.174 + 0.229 + 0.183) / 4 = 0.207 m


Mean or max?

Taking the mean is the obvious first move, and it's what I shipped originally. But in hindsight I'm not sure it's the right aggregate. Averaging pulls the input toward the smaller faces, so for an object whose dimensions vary a lot, the mean sits well below the diagonal and the LOD fades out sooner than it should.

If the spread between the smallest and largest measurement is wide, the maximum is probably the safer choice: it keeps the higher-detail mesh on screen until the object is genuinely small from every angle, which is usually what you want visually. The mean is the more aggressive, more memory-friendly option. Neither is universally correct, it's a quality-versus-cost dial, and the right setting depends on how forgiving your asset class is about popping. I'd expose it rather than hard-code it.


From size to distance

Once you have a size, the distance is trigonometry. The function below answers a single question: how far from the camera does an object of this size occupy a given percentage of the screen?

CalculateDistance(inSize, inScreenSpacePercentage=100, inFovDegrees=45.0, inVerticalResolution=1080):
   ratio = inVerticalResolution / 1080 
   # distance at which the asset fills the screen top to bottom
   distance_to_object     = (inSize * 0.5) / tan(radians(inFovDegrees * 0.5))
   distance_by_screensize = distance_to_object * 100 / inScreenSpacePercentage
   max_distance           = distance_by_screensize * ratio
   return max_distance


Walking through it: distance_to_object is the distance at which the asset exactly fills the vertical field of view, half the object's height over the tangent of half the FOV, the standard "fit a height into an angle" relation. The inScreenSpacePercentage term then scales that out; if you only want the asset to occupy 50% of screen height before switching, it needs to sit twice as far away, hence 100 / percentage. Finally the resolution ratio nudges the distance for higher-density displays, on the assumption your screen-space target was tuned at 1080p.

One implementation gotcha worth calling out, because it bit me: tan() in most math libraries expects radians, not degrees. Feed it 45 * 0.5 directly and every distance comes out roughly 35% wrong. Convert the FOV first, that's the radians() call above.

Plug the mean (0.207 m) into a 90% screen target at a 45° FOV and you get a switch distance of about 0.28 m, the point where this small asset stops being rendered at L0. From there the rest of the chain follows a rule of thumb from Part 1: each LOD step aims for a 50% triangle reduction, so doubling the distance per step mostly holds (L1 around 0.56 m, and so on). It breaks down once the poly count is already low enough that further reduction wrecks the silhouette or the normals, at which point the math stops being the limiting factor and your eyes take over.


Why bother doing this by hand

Simplygon or Unreal will generate LODs for you, so why work it out yourself? Because knowing the math is what lets you debug the tool when its output looks wrong, tune the thresholds to a specific project instead of accepting generic defaults, and stay unblocked when licensing or budget takes the automated option off the table. The tool is faster; understanding why it picks a distance is what makes you useful when it doesn't.

Are there flaws here? Almost certainly, the resolution scaling in particular is a simplification, and the mean-versus-max question is still open. If you've solved either more cleanly, I'd genuinely like to hear it.


© 2026 Stefan Groenewoud - All views are my own, not those of my employer.

Day 35 - 📖 Learning - Week 5 Reflection

General / 19 June 2026

Block 3, first part: tools and pipelines.

Writing about pipelines this week forced me to articulate things I'd been doing on instinct. That's uncomfortable in a useful way. When you have to explain why you structure something a certain way, you quickly find out whether you actually have a reason or whether you've just been doing it out of habit.

I'm still working out what makes my approach to pipelines distinctly mine. Part of it comes down to how I weigh speed against quality: they're not always at odds, but when they are, which one gives first? That's not a question I had a clean answer to before this week. I'm not sure I have a clean answer now either, but I at least know it's a question worth being deliberate about.

The reflection on past decisions was probably the most useful part. Looking back at choices I made on previous projects and tracing why they played out the way they did is more grounding than any amount of forward planning. It's also humbling. There are a few things I'd clearly do differently. Noting that down matters.

One thing I want to keep pushing on is involving external input earlier. I have my own intuitions about what a pipeline needs, but those intuitions are shaped by my own context. End-users think differently. Knowing why something doesn't land for them is often more valuable than knowing why it does land for me.


What clicked

Thinking out loud about pipeline design, on the blog and in the posts, helped me surface assumptions I didn't know I was making. The "How I Structure a Pipeline" and "Documenting Tools for Others" posts in particular felt like they were doing real work: not just explaining a process, but actually refining how I think about it.


What flopped

No images. The NDA makes it impossible to show actual documentation examples or real pipeline screenshots from recent work, and most companies don't publish that kind of material either. It's kept close. So a lot of the posts this week were more abstract than I wanted them to be. I could describe the approach, but I couldn't show it. That's a meaningful limitation for this type of content.


Into next week

Block 3's second part shifts toward the more technical side of pipelines: LOD systems, texel density, mesh instancing, texture compression. More math, more numbers to justify the decisions. Looking forward to it.

© 2026 Stefan Groenewoud. All views are my own, not those of my employer.

Day 34 - 🛠️ Behind the process - Documenting Tools for Others

General / 18 June 2026

Good documentation isn't about explaining your tool. It's about making sure someone else can own it.

Imagine this: someone set up a tool before you arrived. No README, no comments, no context. It works, until it doesn't. And when it breaks, you're reverse-engineering someone else's decisions without knowing what constraints they were working under, what they tried first, or what the tool was never meant to do.

That's the cost of skipping documentation. Not a missed readme. Actual time and money, compounded every time someone new touches an existing system.

I wrote about this briefly on ArtStation a while back - going more into the practicalities and implications of doing documentation for others. This is the expanded version, with more practical detail on what I've actually seen go wrong and how I approach it now.


What Documentation Is Actually For

The default instinct is to write documentation for the tool. That's the wrong approach.

Documentation is for the person who comes after you or the one that has to use it daily. Whether that's a vendor picking up your texturing pipeline, a developer joining mid-project, or you eight months from now when you've forgotten how something works.

That means three things have to be true:

It has to be easily accessible. Information scattered across Confluence pages, Slack threads, and a half-cooked wiki is effectively lost. If someone has to ask where the docs are, they're not going to use them.

Who is your target audience. A developer picking up a tool for the first time needs different depth than someone who worked on the same engine for three years. Writing one document for both audiences means it's useful to neither.

It has to be maintained. Documentation that was accurate six months ago and hasn't been touched since is worse than no documentation. If a workflow changes and the docs don't, you're actively misleading the next person.

None of this is complicated in principle. The failure is usually in assuming that the document you wrote at launch is done.


What Goes Wrong

Most documentation failures fall into a small number of patterns, and they compound.

Ambiguous scope. The document doesn't say what it covers or who it's for. Is this the full material pipeline or just the blend workflow? Is it targeting artists or TAs? Readers spend time in the wrong section or miss the section they needed.

Inconsistent terminology. If the document says "master material" in one place and "base material" in another, and those mean the same thing, readers slow down to verify. If they mean different things and it's not explained, readers make assumptions, and those assumptions produce errors downstream.

Redundancy that creates skimming. When the same information appears in multiple places, readers learn to skim. Once skimming is the default reading mode, they start missing things that only appear once.

Complexity without hierarchy. Dumping advanced workflow steps alongside basic setup instructions treats all information as equally important. It isn't. A new team member trying to set up a material needs to get through basic setup before they can use height blending. Structure the content in the order someone would actually encounter it.

Not accounting for language. Depending on your vendor relationships, the people reading your documentation may not be native English speakers. Passive voice, idiom-heavy writing, and long compound sentences create unnecessary friction. This is easy to fix and almost always overlooked.


How I Structure It

The structure that's worked best for me is a two-axis hierarchy: topic and complexity.

Texturing & Materials/
├── Overview                        ← what this section covers, who it's for
├── Basic Workflow/
│   ├── Simple Material Blend
│   └── Bespoke Texturing
└── Advanced Workflow/
    └── Height Blend


Each section starts with a one-paragraph summary: what this covers, what you need to know first, and who it's relevant to. That alone eliminates most of the "I read the wrong section for 20 minutes" problem.

The complexity split is deliberate. Not every artist needs the advanced workflow. Separating them means the basic workflow stays clean, and the advanced section can go deeper without worrying about overwhelming someone who's just getting started.

For tooling documentation specifically, I add a fourth section: known limitations and open questions. If a tool has edge cases that produce bad output, or if there's a workflow that's undocumented because it wasn't finished, that belongs in the docs. The next person is going to hit those limits. Better they know upfront.


The Iteration Loop

Writing documentation once isn't enough. The loop that actually works:

1. Walk through it yourself, step by step. Follow every documented instruction exactly as written, as if you'd never used the tool. This catches gaps immediately: steps that seem obvious in your head but aren't on the page, missing prerequisites, wrong button labels.

2. Buddy check. Have someone else follow the same steps independently. Not someone who already knows the tool; someone who has the target knowledge level but not the tool-specific knowledge. Watch where they hesitate or ask questions. Those are the gaps.

3. Update when workflows change. Tie documentation updates to tool or pipeline changes, not to a separate review cycle. If a change ships without updated docs, the docs are already wrong.

4. Evaluate blockers in shared assets. If artists are running into issues with shared shaders or the material library, that's documentation signal as much as it's a tooling issue. Recurring questions usually mean the answer isn't where it needs to be.

The goal isn't a perfect document; it's a document that reduces confusion faster than confusion accumulates.


What I'd Do Differently

A few things I'd change if starting from scratch on a documentation project:

Proof of concept before writing. Before writing full documentation, show a one-page layout of the proposed structure and get sign-off. It's much faster to argue about organization at that stage than after ten pages are written.

Visuals from the start, not as an afterthought. Screenshots, annotated diagrams, short video walkthroughs: these reduce confusion significantly, especially for visual learners and non-native readers. Adding them retroactively is tedious. Building them in from the beginning is a better habit.

Delegate by department early. TAs shouldn't be writing the artist-facing workflow documentation and the developer-facing API documentation. Get the right people owning the right sections from the start. Coordination takes effort, but the output is documentation that actually reflects how each team uses the tool.

Don't get attached to what you wrote. Good documentation requires being willing to cut sections, restructure, and rewrite. If a section isn't working, the answer isn't to add more explanation; it's to reconsider the structure. Version control makes this low-risk.


The Real Cost of Getting It Right

Documentation feels like overhead until you're the one debugging a tool someone else built without any. Then it feels like the most valuable thing that could have existed.

The industry is built on handoffs: between teams, between studios, between projects, across years. Documentation is what makes those handoffs work. It's not a nice-to-have for polished pipelines. It's how you prevent the next person from starting from zero.

Build the habit early. Revisit it consistently. And write it for the person who wasn't in the room when the decisions were made.

© 2026 Stefan Groenewoud - All views are my own, not those of my employer.


Day 33 - 💬 Take - Technical Artists Need to Think Like Engineers

General / 17 June 2026

Technical artists sit between art and engineering. The bridge only holds if you can speak both languages fluently.

As a technical artist you are the liaison and bridge between the art department and the rendering or programming team. The value you bring comes from understanding both sides: artists want to push fidelity, programmers are keeping a close eye on performance. Being able to mediate between the two is worth its weight in gold. Not many people have the skill to do that.


More Than Tools and Pipeline

There's a common assumption that technical art is mostly about building tools and pipelines. Get the pipeline running, hand it off, done. But that's only part of the job.

You are often the one translating requests for new feature implementations, or exposing engine capabilities to your department, so the team can actually use them. "Automate that part of the pipeline" sounds simple, but getting it done means knowing how to communicate the ask to engineers clearly and effectively - so you actually get what you asked for.


Talking to Engineers

Engineers respond well to specificity. Vague requests get vague results, or worse, results that technically meet the spec but don't produce what you needed.

Knowing enough about how a system works to describe the problem at the right level of abstraction is a real skill. You don't need to write the code, but you do need to be able to say what you need it to do, what the constraints are, and why it matters. That gets you a better outcome faster.


Talking to Artists

The same principle applies in reverse. If you present a pipeline that doesn't map onto how artists actually work, you'll end up with a tool that nobody uses and eventually gets deprecated.

Understanding what kind of tools and solutions artists are actually looking for - before you start building - saves everyone time. The best way to find out is to ask, watch how they work, and iterate early rather than late.


The Broader Point

The technical part of technical art is obvious. The communication part is where a lot of the actual leverage is. Being able to move fluently between an engineering conversation and an art direction conversation, without losing context in either direction, is what makes a technical artist genuinely useful on a production team rather than just technically competent.


© 2026 Stefan Groenewoud - All views are my own, not those of my employer.

Day 32 - ⚡️ Quick - The Problem with Manual Texture Work at Scale

General / 16 June 2026

Manual processes that work fine for ten assets start failing silently at a hundred. Here is where scale exposes the cracks.


When Manual Works

Manual texturing is the right call when the asset count is low and the assets are mostly hero props, a handful of unique sets, anything where the craft is the point and you'd never want a script flattening your artistic intentions. At ten assets you can hold the whole set in your head, hand-fix an outlier, and re-export without it eating your week. The processes that serve you in the simple situations are also the ones that fail at a hundred.


Where It Breaks Down

Reworking textures at large scale is expensive, and not just the first time you author them. The real cost lands every time something upstream changes. If you switch the method you use to generate AO or normals and, for consistency's sake, you now have to revisit every asset, update it, re-export, and re-validate in-game. Same story if you need to alter the art-style. Do that by hand across a hundred assets and you're not spending an afternoon, you're spending months updating and iterating.

It is easy to miss a few assets here and there. A few assets quietly get missed, bake slightly differently, or drift out of spec, and nobody notices until it shows up in-engine in context, usually next to a correct one where the inconsistency is obvious. Manual work doesn't fail loudly at scale; it just gets a little bit wrong in a hundred places.


What Consistent Scale Actually Requires

The fix isn't "automate everything", that's its own trap (more on that in Day 30 - 💬 Take - Automation). It's consistency first: a defined process where every asset is authored, packed, and validated the same way, so a global change becomes one deterministic operation instead of a hundred manual ones. Only once the pipeline is defined and the edge cases are known is the repetitive part worth scripting.

You can try to 'speedrun' the iteration process but it will catch up to you later, because you simply missed catching the edge cases and how to solve them.


Example

On these bunker environment kits, my tech art director and I spent a few days defining the pipeline: getting the mesh bakes right, testing the system on four assets, locking down the edge cases. Once that was done, we processed 40 asset sets in two-weeks. The consistency we got out of it wasn't just faster than doing it by hand: it was more consistent than hand-authoring would have produced at all.

© 2026 Stefan Groenewoud - All views are my own, not those of my employer.

Day 30 - 💬 Take - Automation

General / 11 June 2026

Automation is great for redundant processes and ensuring consistency. But there's a right time and a wrong time to reach for it.

I want to share some thoughts and past experiences on automating processes, specifically where I've seen it go wrong and what I've learned from it.


Why Consistency Matters

The fewer one-off cases and derivatives a pipeline needs, the more scalable it is. Consistency leads to predictable outcomes and predictable memory usage. I've seen this proven in shader development; fewer permutations mean less complexity to manage, lower compile times, and a more stable pipeline overall.

result of the tool I wrote to batch swap shaders and update their textures.


Too Much Too Soon

That said, I see too many developers reaching for automation before the pipeline is actually ready for it.

The order matters: first, you need to understand and fully define the pipeline you're trying to streamline. Only then can you figure out how to get there: how much time the code will actually take, how to break it into manageable pieces, and what the edge cases are. I get it, I'm also eager to start writing code. But on a professional project, that instinct can cost you. The more you understand upfront, the less code you'll end up throwing away.

This also requires alignment. Having a shared understanding with your peers and tech directors before you start building is important; otherwise you risk building something that doesn't serve anyone and quietly gets abandoned.

Nobody likes throwing away work, and companies especially don't. The problem with automating too early is that people get precious about a tool once it's been delivered. When that tool eventually needs reworking, the reaction becomes "but we just built that", creating friction that makes it harder to do the right thing. Sometimes you have to bite the bullet anyway. If you don't, you end up carrying tech debt: a tool that's half-baked, a workflow that's awkward, and a shipping schedule that keeps you stuck with both.



Conclusion

Automation itself isn't the problem; the sequence is. Build consistency into the pipeline first, get the team aligned, then write the code. The pipeline definition is the hard part; the code follows from it. The question worth asking before you start isn't "can we automate this?"; it's "do we understand this well enough to automate it?".

© 2026 Stefan Groenewoud - All views are my own, not those of my employer.

Day 29 - 🔬 Deep dive - Surfacing Pipeline End to End

General / 10 June 2026

Surfacing is everything between a finished model and a validated asset in-engine - and a repeatable pipeline beats per-asset improvisation every time.


What "Surfacing" Actually Means

Ask three studios what "surfacing" means and you'll get three different answers. At one studio it's purely the artistic side: developing materials. At another it includes the technical side too: figuring out which textures need to be created and where they go, running performance tests, setting up the pipeline itself. At others, a programmer handles all the foundational setup and the artist never touches it.

Both models have pros and cons. When the technical side is handled for you, you can focus entirely on the craft but you never get the full picture of what your data is doing, how it's being applied, and it usually means less artistic freedom with the shader: most likely you get handed a black box, built around performance first. It's also why the job is hard to explain when applying elsewhere, the title stays the same, but the expectations shift completely per company.

For this post, surfacing covers the whole span: from reference gathering through authoring, packing, engine setup, and optimization, ending at a validated asset in-engine. That's deliberately wider than "just texturing", texturing is one stage in the middle of it.

This is how I've seen it work across studios and conversations with others in the industry - your mileage may vary.


Inputs and Prerequisites

A shader can't fix what arrives broken. Before you can start authoring, a few things have to be true: clean topology, non-overlapping UVs; an agreed texel density target (coming in one of the future blogposts); a naming convention everyone follows; and reference gathered and approved by art direction. Each of these is cheap to enforce at intake and expensive to fix after twenty or more assets are built.

As an example: I once had an asset where messed-up topology and wonky normals propagated into the bakes, making the curvature read far too extreme and throwing off other features in the shader. It made the real problems hard to see through the noise - a perfect example of a broken input that no later stage could hide.


Reference and Calibration

I've covered the why in Day 11 - 💡 Insight - Why Photo Reference Changes Everything and the how in Day 14 - ⚡️ Quick - Using Gloss Meters for Surface Measurement. In the pipeline, what matters is the when: values and gloss get locked (ideally) before authoring starts, not adjusted afterward to taste. These decisions also depend on where you actually settle with the art style, but the sooner you can lock down the rules, the less you have to keep iterating on assets. If calibration lands when you're already a few assets in, those assets need to be reprocessed to bring them back into the correct ranges.

Calibration also needs a where and a who. The where is a neutral, controlled lighting scene in-engine: PBR standardizes the physics, but every engine has its own roughness curve and tone mapping quirks, so values that look right in Substance can read different when rendered in engine. The who is art direction: a value isn't calibrated because it looks right to you; it's calibrated when it's signed off and becomes a rule the rest of the pipeline can trust.


Material Authoring

The core authoring stage and the one I've written about most, so I'll keep it to the pipeline view: the approach in Day 6 - 🛠️ Behind the process - Approaching Material Creation, the library structure in Day 12 - 🛠️ Behind the process - Building a Material Library. The pipeline question at this stage is reusable vs. bespoke: does this asset pull from the shared library (fast, consistent, one point of update) or does it justify bespoke work (hero assets, unique storytelling surfaces)? The ratio between those two is a budget decision, not an artistic one.

Do you need full texture bake-downs, or can it be composited in-engine? Compositing gives you more flexibility in propagating changes from the material library, but less room for bespoke details or storytelling - things will look a bit more generic, but consistent across the board. My preference is to first build the base materials that will dictate 70-80% of the materials you expect to see in your game; you can still reference those in Painter or Designer when texturing your bespoke bakes, or simply use them directly as composited materials in-engine.

There are always edge-cases that will throw a wrench in your pipeline; that is to be expected. Not all pipelines transfer equally between asset-types; characters need a different setup than your environment kits.


Texture Sets and Channel Packing

The in-tool vs. in-engine packing tradeoff is in one of the upcoming blogposts. What belongs here is the convention itself: which maps go in which channels, and why it must be project-wide rather than per-asset. Resolution decisions follow from texel density targets, not from what looks nice in isolation. And alpha is never free, an alpha channel doubles BC7 block cost considerations and should be a deliberate choice per texture set.

Depending on the quality bar, I would pack the color separately, but pack normals with roughness and the metalness mask in a BC7, it gives me good precision for what I need. If I need custom masks, I can pack them with AO in a BC3 at half resolution. Not everything needs to be at max res; some maps can be lower resolution because no one ever sees them up close, or we cover it up with tricks in the shader. All context- and asset-type dependent.


Shader and Engine Setup

Where the textures meet the renderer. The recurring tradeoff: permutations vs flexibility. Every exposed parameter makes the master material more flexible and the shader more expensive; every hardcoded decision makes it cheaper and more rigid. The same question applies to what needs to change per instance. The pipeline answer is to expose what varies per asset and lock what doesn't. The same goes for where materials get assigned (in the DCC vs. in-engine): which side you land on matters less than that it's decided once, for everyone.

Typically I would expose: tiling rate, color or tint, and settings for parallax occlusion mapping. Some of these are per-instance dependent, some aren't. Exposing certain texture inputs is also helpful - the more code or nodes you can reuse, the better.


Optimization and LODs

By this stage, the work is mostly mechanical if the earlier stages were consistent: compression, mip generation, and fading features out over distance to strip cost - lower-resolution sets or dropped alpha for far LODs, driven by the pixel-coverage math in Day 7 - ⚡️ Quick - Pixel Coverage Calculations and the LOD decisions that follow from it. The point for the pipeline: optimization is a pass, not a rescue. If it's where problems get discovered, the validation gate is in the wrong place.


Validation Pass

The QA gate before anything ships: automated checks for texel density in range, missing or duplicate maps, naming, resolution budget - and human eyes for the things scripts can't judge, like whether a material reads correctly in context. The short version: define what "valid" means, script what's scriptable, and make the gate impossible to skip quietly. I'll be putting this to the test against a real asset set in an upcoming post.


Handoff and Documentation

A pipeline that only works while you're in the room isn't a pipeline - it's a dependency. The principles are simple: document for the person who comes after you, keep it accessible, keep it maintained (a topic that deserves its own post, and will get one). For surfacing specifically that means: when to assign which shader, what are the exceptions in the pipeline, the texture packing methods, the texel density targets, the naming rules, and the validation criteria all written down where a vendor or new hire finds them on day one.


Closing Thought

Every stage of this pipeline gets cheaper when the stage before it is consistent. It's kind of like compound interest: the consistency accumulates, and it pays off in performance and lower development cost because there's less iterating needed. Locked reference makes authoring decisive. Consistent texel density makes packing and resolution decisions automatic. A followed naming convention makes validation scriptable. Consistency upstream is what makes the downstream cheap - improvisation just moves the cost to whoever touches the asset next.


© 2026 Stefan Groenewoud. All views are my own, not those of my employer.

Day 28 - 🧪 Experiment - Same Scene 3 Lighting Setups

General / 10 June 2026

Same geometry. Same materials. Three completely different reads. Lighting is not a finishing step, it is a storytelling decision.


The Scene

I grabbed the City Subway Train scene from the Unreal Marketplace to get started quickly. The core challenge became clear almost immediately: I wanted the outdoor environment visible through the windows while still reading the fluorescent interior lighting. That is a real constraint in photography and rendering — interior and exterior cannot both be correctly exposed at the same time. Depending on time of day and weather settings, one will always be overexposed relative to the other.

Original scene by Dekogon Studios


Unreal lets you compensate by setting light intensities against relative EV values, so you can nudge specific elements back into a readable range. Even so, having the exterior at 1500 nits and the interior at 3000 nits produces a visibly underexposed exterior — technically correct, but not what you would want artistically.

The scene was not set up to physical light measurements, which made sense given it was authored in Unreal 4.17. I did not touch color grading. Everything here came down to Lighting, Exposure, and Camera settings.


Setup 1 - Night-time

Order: Lit, Lighting only, HDRI Skybox

At night, the HDRI is almost irrelevant — you can barely make out the outside through the windows. The brightly lit interior completely dominates, which creates a sense of enclosure: the world outside has disappeared and the train car is all there is. I used correct EV values to set the baseline before adjusting.


Setup 2 - Sunset

Order: Lit, Lighting only, HDRI Skybox


Sunset was the trickiest to balance. The interior is still the dominant light source, but warm directional light through the windows starts competing with it. I used the Sunny 16 rule as a starting point (it assumes full midday sun, so I adjusted down for sunset), then compensated the HDRI intensity to keep the exterior readable. The principle: with the camera exposed for the interior at EV 5, an exterior that reads correctly at EV 10 needs its intensity boosted by 2^(10−5) = 32× to land in the same displayable range. The warmth of the sunset pushing in against the cool fluorescents gives the scene some tension that the other two setups lack.


Setup 3 - Cloudy

Order: Lit, Lighting only, HDRI Skybox


The cloudy setup looked the most washed out. The HDRI brightness sits around 10,000 nits, which is substantial, but soft omnidirectional light kills any sense of directionality. The tinted, dirty glass of the windows also dims incoming light, so I compensated the intensity slightly. The result is flat and even — which is the nature of overcast light. No drama, no shadow, no story. It is the least interesting of the three, but that is the point: diffuse light removes contrast, and contrast is what makes a scene readable.


What This Shows

Each setup uses the same geometry and materials, and yet they do not feel like the same place. The night setup feels enclosed and isolated. The sunset setup feels transitional — somewhere between the world inside and the world outside. The cloudy setup feels institutional, utilitarian, like a line you take every day without noticing.

That shift comes entirely from lighting choices — not from new assets, not from color grading, not from post-processing. These setups are rough and I am not a lighting artist. But the experiment made the principle concrete: lighting does not just illuminate a scene, it determines what story the scene tells.


© 2026 Stefan Groenewoud - All views are my own, not those of my employer.