提供: wiki
移動先: 案内検索

Proposal: DupliMats

(For alternative names for this feature, see the section "The Term 'DupliMats'" below.)

(For a test render animation using this feature, see the section "Rendering DupliMats".)

The Problem

With the DupliVerts feature, it is now quite practical to develop complicated model scenes with thousands of identical parts. This points to the strength of the computer: handling automated tasks. Unfortunately for those who like more organic realism in their scenes, this also points to the weakness in most computer rendering: handling the subtle natural variations found in every unique object.

Let's take some simple examples: a brick wall, a tile floor, a wooden slat fence. These are prime candidates for DupliVerts to model quickly: make a brick, make a tile, make a wooden plank, and duplicate them. Wait for a quick preview render, and the flaw becomes obvious. Real bricks, real tiles, real wooden planks are all a little bit different. The eye spots the artificial nature of the scene right away.

A bunch of unrealistically identical blocks of wood.

Textures today can offer vertex manipulation. This is done by taking each vertex and tweaking the (X,Y,Z) location just before raytracing or other rendering calculations are done. With DupliVerts, this is no different, but each vertex may be arranged differently within each duplicated virtualized object.

If Materials could somehow allow this kind of manipulation of the associated texture values, then each object (and each virtualized DupliVert object) which used the texture could appear subtly different. Just as with vertex deviation, it generally doesn't matter to the artist where the grain lines are in the wood, but each board can't have the same grainline. It often doesn't matter how red each particular brick ends up, as long as they are all different colors within some reasonable bounds.

The Current Workaround

The first attempt to fix this is by adding subsurfacing and vertex-deviations features to the materials. Unfortunately, at small deviation values the effect is almost unnoticeable, and at large deviation values, major vertex placement artifacts can cause serious problems. And this approach still doesn't address the material problem: grain is repeated, hue is repeated, texture is repeated. And if you try to use "Global" texture space instead of "Object" or "Orco" texture space, then the wood's grain will crawl unrealistically if it is ever moved through an animation.

The second attempt to fix this is by breaking DupliVerts down.

  • hit the Apply > Make DupliVerts Real button
  • select all the objects and Unlink all the identical objects and their materials
  • tweak a few brick-material copies, a few tile-material copies, a few plank-material copies
  • hope that you don't need to move or change the number or arrangement of the copies
  • wait while the system chugs down with thousands of nearly identical objects and materials

The 'DupliMat' Solution

The goal here is to allow someone to make a realistic brick wall with just one brick material, or a hand-assembled tile floor with one tile material, or a weathered wooden fence with one wood material. This proposal shows how this material diversity effect might be achieved in the object model and the user interface.

Modeling DupliMats

If the goal is to allow one Material to do the work of many, then the key feature will be found in the material itself, and the textures which that material employs.

One naive implementation may simply offer a "diversity amount" or a "deviation range" for each and every value which is allowed to be selected for each unique rendered object. For example, the user could allow the Red Diffuse channel to vary between 0.5 and 0.95 for red bricks. However, this would make for a completely incomprehensible user interface (even by Blender standards) with dozens of changes to the implementation and interface to allow for all these new parameter numbers and sliders.

I propose instead that we offer a few bit-flags for values which are allowed to stray. This makes for one new bit-flag member in each texture structure. It also reduces most of the user interface to the addition of tiny little toggle buttons (which I label or D plus-or-minus).

There should still be a numerically tunable range, but I further propose that this facility be fairly minimalistic, in the interest of simple implementation and simple user interface. With one number for each Material, plus one number for each Texture channel in the Material, a huge array of unique material tweaks is possible, and the concept can be readily explained in documentation and tutorials.

The Material object needs the following data support for Diversity.

  • Material needs one Diversity number for itself
  • Material needs a small set of bit-flags to indicate which core values are made Diverse

To let Textures build on this facility, these features work similarly.

  • Material needs one Diversity number per Texture channel slot
  • Texture needs a small set of bit-flags to indicate which texture values are made Diverse

Rendering DupliMats

Referring again to the goal: each rendered object or virtually duplicated object must appear to have a different material. Within the bounds set by the Diversity parameters just mentioned, it does not matter what particular values are chosen for each object. However, when rendering an animation, it is important that a given object uses the same set of values in each frame. You would not want a brick wall to blink and flash like a casino billboard every time the camera advanced.

At render time, this requires the generation of a unique number for each rendered object, but to be able to recreate that number perfectly for each frame of an animation. I'll call this number the object Hash for this discussion.

I propose that the combination of the Object's string name and the ObData's string name is a good source of unique identity for most objects to be rendered. In the case of a DupliVert-virtualized object, we would need to combine that name with the iterator through the vertices. If there are other ways for a single Object/ObData pair to be rendered in multiple places in one frame, then of course this iteration information would need to be combined into the DupliMat renderer's object hash.

If the artist manually or inadvertently changes an Object name, or an ObData name, then the shade of a particular brick may change, or the grain through a particular board may shift. If the number of vertices in a DupliVert mesh changes, then some or all of the remaining virtual bricks or boards may or may not change, depending on how those internal iterators work. I think this is an acceptable risk for most of the uses of DupliMats which I could think of, but if this turns out not to be the case, then a new and permanent (and even user-configurable) scheme can replace the method of determining a unique object hash value.

Once there is a simple way to generate a unique but stable object hash value for each rendered object, the rest of the rendering process is quite straightforward. Take the object hash and normalize it to the total possible range of [-1, 1]. For each texture channel involved in producing the material, multiply that normalized hash against the Diversity power for that texture channel. Then perturb each D±flagged texture by that bias amount.

Roughly, ignoring the actual methods of ray hit testing and whatnot, the procedure is then as follows.

  • iterate through all top-level objects on selected layers,
    • iterate through all duplivert instances of children objects,
      • compute the object Hash for this virtual instance of an obdata being rendered,
      • normalize the Hash so it is in [-1, 1],
      • iterate through the materials,
        • if the material has its core Diversity != 0.0,
          • vary each D±flagged material Value += Diversity * Hash (and clamp),
          • iterate through the texture channels,
            • if the material has a texture channel Diversity != 0.0,
              • vary each D±flagged texture Value += Diversity * Hash (and clamp)
DupliMats allows parameters to vary pseudo-randomly on each object.

Voila, each wooden object looks more organic and each brick looks naturally unique.

Here is a new example.

A 'clay' material has some core values and two texture channels for sediment 'swirls' and surface 'smears'.

This requires three Diversity number slots: one for the core values, and one for each texture channel. Perhaps one of these Diversity numbers is set at 0.0, so it is effectively not used. The core Diversity number of 0.8 allows the D±flagged core values to vary within N-0.8 to N+0.8 for each rendered object made with this clay material. The texture slot Diversity number of 1.3 allows the D±flagged values inside that texture to vary within T-1.3 to T+1.3 for each clay object.

  • The Material has a Diversity number set to 0.8 by the user
  • The Material has D±flag toggles for each of the core values which the user wants Distinct
  • The Material has a Diversity number set to 0.0 by the user for the 'swirls' Texture channel
  • The Material has a Diversity number set to 1.3 by the user for the 'smears' Texture channel
  • The Texture has D±flag toggles for each of the texture values which the user wants Distinct

Adjusting DupliMats (the User Interface)

This is a view of the final implemented user interface, which is significantly simpler than the original "D+" buttons version I mocked up. This version just adds four widgets to the Texture panel.

Adds just four widgets to the Texture panel.

Shown above is the existing Texture panel of the Blender interface. It sports a new pair of widgets at the default settings, which indicate zero diversity (a normal material). The buttons can control the diversity of several parameters or combinations, such as "Vary Specular Amount" and "Vary Diffuse Color." The chosen attributes are allowed to vary up or down by the given amount.

Further, once a texture channel has been assigned a texture, two additional widgets show the settings for that channel's diversity. They work the same way, but control the parameters of the material's texture channel, i.e., those generally found in the "Map Input" and "Map To" panels.

This interface does not offer complete support for all possible ways you could make a material or texture vary, but in practice, only one or two parameters in a material need diversity, anyway. Through the judicious use of diversity, a lot of interesting material effects can be achieved.

The figure above shows the interface after choosing the most common real-world combination: let the material's overall diffuse amount vary, giving each object instance some diversity by appearing lighter or darker than its neighbors, and let the texture channels vary by shifting them relative to the object at random. Of course, there are many other combinations which would serve useful purposes for adding realism (or at least, chaos) to material-rich scenes.

Coding the DupliMats Feature

I've been using Blender as an artist for a while. I am also a proficient C/C++ coder, but this has been my first real chance to look at the Blender codebase in depth.

I have reached an 80% solution for the first revision of DupliMats but need to check on bugs and performance, and finish diversity values for the texture channels.

This first implementation animates 81 cubes using just one material.
A wood plank floor, each board is a bit different.

The Term 'DupliMats'

I am not attached to the name DupliMats, and I know it may have certain connotations to existing Blender developers and users. Some other suggested words would be Material "Distinctiveness" or simply Material "Diversity." If you like that better, or have another idea, start the discussion.

DupliMat sounds like a perfectly suitable name for something which allows one Material to act like a bunch of virtually distributed materials. A key difference, however, is that other Dupli features give the artist supreme control over the specific outcomes of a gang of duplicated objects, whereas this feature gives a bit of pseudorandom-ness to the results.

I will note that someone used the name DupliMat for a script, though I didn't even check if that script attempts similar results through automated copying of material blocks.