什么是烘焙? 简单地说, 就是把物体光照的明暗信息保存到纹理上, 实时绘制时不再进行光照计算, 而是采用预先生成的光照纹理(lightmap)来表示明暗效果. 那么, 这样有什么意义呢?
好处:
当然, 缺点也是有的:
肯定不只这几点,但我暂时只想到这几点
那么怎么生成lightmap呢?
最直接的办法: 光线追踪....(原理想想很简单, 按照物体定律来就可以了)
但是光线追踪这东西......就算用来离线生成我都嫌慢-_-
下面说的这个是利用GPU进行计算的, 跟实时光照没什么两样:
原理:
想想实时渲染的顶点变换流程: pos * WVP之后, 顶点坐标就变换到屏幕空间了[-1, 1]
如果VertexShader里直接把纹理坐标做为变换结果输出(注意从[0,1]变换到[-1,1]), 那么相当于直接变换到了纹理坐标系, 这时在PixelShader里还是像原来那样计算光照, 输出的结果就可以拿来做lightmap了
示例:
这是一个典型的Phong光照模型下的球(这里不考虑阴影效果, 对它不需要进行特殊处理):
这是VS:
VS_OUTPUT vs_main( VS_INPUT Input ) { VS_OUTPUT Output = (VS_OUTPUT)0; Output.Position = mul( Input.Position, matViewProjection ); Output.Texcoord = Input.Texcoord; float3 fvObjectPosition = mul( Input.Position, matView ); Output.ViewDirection = fvEyePosition - fvObjectPosition; Output.LightDirection = fvLightPosition - fvObjectPosition; Output.Normal = mul( Input.Normal, matView ); return( Output ); }
把原来的WVP变换改成变换到纹理坐标系:
//Output.Position = mul( Input.Position, matViewProjection ); // transform to projection space Output.Position.xy = Input.Texcoord * float2(2, -2) + float2(-1, 1); Output.Position.w = 1;
输出的结果就成这样了:
保存下来可以直接使用. 这里我用的模型比较特殊, 本身的UV就满足前面提到的条件, 所以直接跟原纹理叠加就可以. 当然, 如果只保存明暗信息的话, 就不影响原纹理的复用, 因为通常lightmap不需要很高的精度:
有了lightmap, 再次画的时候就简单了, 只需要贴纹理, 光照大可以关掉:
如果还想要一更好的效果, 可以加入一些实时的全局光照算法, 如Dynamic Ambient Occlusion之类...阴影同理...