Sign up to our twice-monthly newsletter today for the latest tutorials, interviews and product information.

Advanced Baking - Baking Next-Gen lighting with Turtle

By Illuminate Labs

Web: Open Site

Date Added: 9th December 2009

Let's look at how you would bake an ordinary PTM texture with Lua, so you'll get an overview of the parts comprising a Lua Bake Script. We're going to use the ArmorBaking.mb scene again, so prep Source and Target Surfaces for a Surface Transfer if they are not already. Switch everything but the Lua baking off in the Outputs. All Lua Baking Scripts must implement one or more of the following functions:

Let's take a look at a simple Lua Bake Script.

function setup()

bset("gather.sampletype", "direct dynamic illumination")

bset("gather.space", "tangent space")

bset("gather.distribution", "equiareal")

bset("gather.minsamples", 100)

bset("gather.maxsamples", 200)

bset("gather.coneangle", 180)

bset("gather.cosweighdynamic", true)

bset("output.size", 6)

bset("basis.type", "custom")

bset("basis.rgb", false)

end

function basis(x, y, z)

values = {x*x, y*y, x*y, x, y, 1}

return values

end

function bake()

ci = getBasisCoefficients(0)

return ci

end

The function setup() contains a series of bset statements, which set the options for the Turtle baking. The Turtle Reference Manual will be your best friend here, since you'll probably not be able to remember all the options from memory. The gather.sampletype attribute controls the type of illumination that Turtle will sample; it's currently set to sample direct dynamic illumination. Dynamic illumination means that we wish to consider any possible lighting direction, which is suitable if we wish to bake down a PTM, for example. We can also bake down indirect dynamic lighting, but this requires us to correctly setup a Dynamic Photon Map in the Render tab, which will generate an emission of photons down onto your scene from all directions.

You can also specify indirect illumination, which will bake down the static indirect illumination in the scene, which is what you'd want for baking an RNM. The gather.space attribute defines the coordinate system in which you want Turtle to sample in. It's currently set to the tangent space defined by the low-res mesh, but it can also be set to object space or a coordinate system based on the sampled source surface. The gather.distribution attribute controls how the samples are spread out over the sphere, the equiareal option samples equally in all directions, but you can choose to use a cosine weighted distribution as well, which can be useful if you are baking occlusion maps. Turtle will automatically make an adaptive sampling between the gather.minsamples and gather.maxsamples, so be sure to set both in your Lua scripts, otherwise the default values will kick in, which might give you unexpected results. The gather.coneangle attribute will restrict the sampling to a subset of the sphere, so setting it to 180 will result in hemisphere sampling in the chosen coordinate system. You can weigh the actual sample data by a cosine weight by enabling the gather.cosweighdynamic property. You have to manually set the output.size property for your baking script, because in many cases Turtle will not know how many texture components you want to write out. You select a basis for your sampled information through the basis.type property. We've specified that we want to use a custom basis, but there are also predefined basis for PTMs and Spherical Harmonics. If you are only interested in the intensity of the illumination, basis.rgb should be set to false, otherwise you could have triple the amount of output components to deal with.

The basis(x, y, z) function is really very simple. Turtle will call your basis function with a given vector v = [x, y, z] that defines a point on the sampling sphere, asking you what values this vector will produce in your custom basis. All you have to do is plug the values into your function and return the number of coefficients set by the output.size property. The sampled data will automagically be fitted to your basis during rendering, so there's really nothing much to it. Recognize the basis by the way? It's the standard PTM basis!

The bake() function is where most of your custom work is done, although it is very basic in this script. We get the coefficients for the intensity by using the getBasisCoefficients(0) function, and pass them right out for Turtle to write out. Passing a value of 1-3 to the getBasisCoefficients(...) function gets you the respective color channel coefficients. For such a simple case as this, we don't actually need to implement the bake() function, so it could have been skipped completely.

With Model View Hardware Visualization enabled, Lua baked textures are automatically connected to the Color property of the Hardware Shader. Go ahead and switch this off and render out a frame using the PTM Lua Bake Script. You will have to manually connect EXR channels 0-2 to Light ABC, and EXR channels 3-5 to Light DEF. With Hardware Shading on, you should now get something you recognize.

Just having duplicated PTM baking through a Lua Bake Script isn't much fun, but we can do some interesting things now that we have the base script going. There's a nice trick in the original PTM whitepaper called Diffuse Gain that is quite easy to implement. It essentially boosts the effect of the PTM by scaling the PTM coefficients appropriately. Change the bake() function to:

function bake()

ci = getBasisCoefficients(0)

fMaxX = (ci[3]*ci[5] - 2.0*ci[2]*ci[4]) / (4.0*ci[1]*ci[2] -

ci[3]*ci[3])

fMaxY = (ci[3]*ci[5] - 2.0*ci[1]*ci[5]) / (4.0*ci[1]*ci[2] -

ci[3]*ci[3])

diffuseGain = 1.4

co = {}

co[1] = diffuseGain * ci[1]

co[2] = diffuseGain * ci[2]

co[3] = diffuseGain * ci[3]

co[4] = (1.0 - diffuseGain) * (2.0*ci[1]*fMaxX + ci[3]*fMaxY) + ci[4]

co[5] = (1.0 - diffuseGain) * (2.0*ci[2]*fMaxX + ci[3]*fMaxY) + ci[5]

co[6] = (1.0 - diffuseGain) * (ci[1]*fMaxX*fMaxX + ci[2]*fMaxY*fMaxY +

ci[3]*fMaxX*fMaxY + (ci[4] - co[4])*fMaxX + (ci[5] -

co[5])*fMaxY + ci[6]

return co

end

The diffuseGain property controls the amount of boosting; a value of 1 will give standard PTM, unboosted, so try out some different values. Render out a frame and compare to the old PTM without boost to see the effect.You start to see artifacts if you go as high as 1.4, as this is only a rescaling of the effect, but at least you'll get the basic idea at least.

Taking full advantage of Turtle using Lua Bake Scripts will require you to spend some time on the theory and details of newer computer graphics concepts such as Spherical Harmonics, Wavelets or other applicable mathematical tools. For your everyday needs you might be better off using the preset baking passes, but you never know, the need to develop your own custom illumination model might arise sooner than you think! The Lua Bake Scripts will allow you to experiment way faster than when having to write your own proprietary tools.

Check out the RNM baking script

function setup()

bset("gather.sampletype", "indirect illumination")

bset("gather.space", "tangent space")

bset("gather.distribution", "equiareal")

bset("gather.minsamples", 200)

bset("gather.maxsamples", 300)

bset("gather.coneangle", 180)

bset("output.size", 12)

end

function bake()

-- RNM basis vectors

basis1 = vec3(0.8165, 0.0, 0.577)

basis2 = vec3(-0.408, 0.707, 0.577)

basis3 = vec3(-0.408, -0.707, 0.577)

-- Project indirect light onto our RNM basis vectors

rgb1 = getSampleProjectedSum(basis1, false)

rgb2 = getSampleProjectedSum(basis2, false)

rgb3 = getSampleProjectedSum(basis3, false)

-- Weigh the indirect contribution

n = getSampleCount()

rgb1 = (rgb1 * 2.0) / n

rgb2 = (rgb2 * 2.0) / n

rgb3 = (rgb3 * 2.0) / n

-- Add direct light contribution

nlights = getLights()

for i = 1, nlights do

col = getLightCol(i)

if (getLightAmb(i)) then

rgb1 = rgb1 + col

rgb2 = rgb2 + col

rgb3 = rgb3 + col

else

localLightDir =

normalize(worldToGather(getLightDir(i)))

weight = dot3(basis1, localLightDir)

if (weight > 0) then

rgb1 = rgb1 + col * weight

end

weight = dot3(basis2, localLightDir)

if (weight > 0) then

rgb2 = rgb2 + col * weight

end

weight = dot3(basis3, localLightDir)

if (weight > 0) then

rgb3 = rgb3 + col * weight

end

end

end

array = {rgb1[1], rgb1[2], rgb1[3], 1.0,

rgb2[1], rgb2[2], rgb2[3], 1.0,

rgb3[1], rgb3[2], rgb3[3], 1.0}

return array

end

< previous page continued on next page >

Related Tutorials

Tutorial

The Making Of 'CGRecord CH1'

Keywords: Making of, Project, Arch-viz, Building, Architecture, Lighting, 3ds Max

Tutorial

Making Of 'Robot'

Keywords: Robot, Project, Making of, Lighting , Hard-surfacing, Render, ZBrush, Photoshop

Tutorial

Mudbox 2009 Quick Start Series: Baking Ambient Occlusion Maps In Mudbox

Keywords: baking, ambient occlusion, ao, creature, head,

Readers Comments
(Newest on Top)

No
comments yet. Be the first to comment!