今天仔细研究了 Shaowgun 示例中那个金黄色雕像所使用的光照纹理烘焙工具:“Render To Texel Baker”。因为要在移动设备展现比较逼真的光照效果,但是实时使用法线贴图并大量用于场景建筑中,对于移动设备还是有些力不从心。于是原作者写了这个工具,使用法线贴图和多个光源来对模型的贴图进行光照烘焙并保存烘焙结果。

  作者直接读取模型的所有三角形,顶点,uv,法线和切线数据,以及提供的模型法线贴图(最终转换到世界坐标系,计算过程证明了我之前对于tbn矩阵相关计算的理解是正确的),然后针对每个三角形中的每个像素,都通过插值和权重来计算出这个像素的世界坐标位置和世界坐标法线,并分别将每个像素的位置和法线各自存编码储到纹理里面,纹理的大小都和提供的法线贴图相同。最后将这些所有的数据以及模型的主要贴图都赋给材质,运用多个光源和观察者的位置来多次渲染这张主贴图,每次一个光源,每个光源多个观察者位置,但是每个光源的光照强度都已经根据光源个数做了平均,这样最终的渲染结果是各个光源渲染效果的加权平均值。

  其中作者的有些算法比较有意思,比如每个三角形中某个像素的顶点位置如何根据三角形的三个顶点的权重来计算呢?作者根据面积来计算的,对于三角形abc的面积若为s,其中一点p,p点对于顶点a的权重ma为:三角形pbc面积/s,以此类推计算mb,mc,这样点p的位置vp为: a*ma+b*mb+c*mc。我并不知道dx和ogl里对每个像素的位置如何差值计算,是否就是使用的这个算法还要再去查阅资料,不过这个算法很有意思,三角形面积的计算使用的是“海伦公式”。

  对于每个三角形的uv坐标,作者找出u和v方向的最小和最大值,构成一个四边形,然后逐个像素遍历看该像素是否在三角形内才处理,判断是否在三角形内的方法也很简单,对三角形abc,任意一点p,如果向量pa,pb的夹角,pb,pc的夹角,pc,pa的夹角之和为360度,则p点在三角形内部,如果在外部,夹角之和是小于360度的。

  还有作者如何将世界坐标的位置编码到纹理里面也比较有意思,作者找出整个模型的所有顶点中的x,y,z的最大最小值,这样对于任意一点p,就可以作为最大最小值之间对于参数t的差值结果,即:p = slerp(min, max, t); 如此可知 0<= t <= 1,计算出t就可以直接写到纹理中。

  关于接口 void Graphics.Blit(source : Texture, dest : RenderTexture, mat : Material, pass : int = -1),开始始终不明白为何作者使用时第一个参数source使用的null,对于官方的说明:“Blit sets dest to be active render texture, sets source as _MainTex property on the material, and draws a full-screen quad.” 也没怎么看懂,尤其是 _MainTex 和 source 的关系,后来经过反复修改测试明白,source 这个参数不是直接就被拷贝到 dest 中,而是说,如果你填充了 source 参数,那么该接口会把你使用的材质 mat 中的 _MainTex 修改为 source。我将 mat 中的 _MainTex 设置为空,运行后 _MainTex 果然被自动设置为了 source,而当我把 mat 所使用的 shader 中的主纹理 _MainTex 的名称改成随意的名称比如 _MyTex,同时讲纹理设置为空后,运行 Blit,发现 _MyTex 丝毫未动并未被修改为 source,因为 Blit 没有找到 mat 中有名为 _MainTex 的纹理变量,修改失败同时所设置的 source 将无任何效果,所以也就明为了作者代码的含义:Blit(null, dst, mat, -1) 将 source 设置为 null 后该参数无效,mat 中的 _MainTex 不会被修改(其实在编辑器中已经手动指定了一张纹理)。

  对于烘焙所使用的 shader 没什么特别,里面跟一个 Bumped Specular 的 shader 没啥两样。当然你可以自己根据想要的效果扩展或重写。

  烘焙所使用的法线贴图导入格式作者要求是 ARGB32 (选择 Automatic TrueColor),我试验后发现,这个格式仅在编译目标是 PC&Mac 时是这样,我切换到 IOS 下,选择 Automatic TureColor 后,实际的格式变成了 RGBA32。

  最后无意中看到一个关于"_MainTex_ST"的问答帖子

  还有一点:pixel space - (0,0) is lower left.

  作者对于工具的使用建议:

  1.加入尽可能多的光源,同时使用多个观察者位置;

  2.对于烘焙完毕的纹理还可以拿到 PhotoShop 中修理调整;

  3.可以同时烘焙多个不同参数的目标,还可以对不同的烘培结果之间再次烘焙洪培混合,以及运行时动态调整 shader 中的颜色等参数,不断试验以便寻找到满意的结果。

  工具目前存在的问题:

  1.不支持多层 uv 和 阴影;

  2.目前仅包含一个 Bumped Specular 的 shader;

  3.目前仅支持点光源;

  4.有的模型烘焙起来可能需要的时间较长;

  5.烘焙完毕的纹理可能会产生缝隙(尤其是贴在模型上以后比较明显,我后面的例子中会看到)。

  最后附上一张我试验的结果:

  

  左:烘焙前;右:烘焙后

 

Render To Texel Baker的更多相关文章

  1. React.render和reactDom.render的区别

    刚开始学习react.js.发现网上的资料,有些是写着react.render,有些写着reactDom.render.觉得很奇怪就查阅了一下资料.解释如下: 这个是react最新版api,也就是0. ...

  2. XF custom render 各平台实现类

    目前的XF还是非常简陋的,所以存在大量的自定义工作.一般情况下我们只是要需要派生原生的XF控件,然后在各平台下修改其呈现方法. 所以了解每个XF控件在不同平台上呈现使用的控件类是有所必须要的.以下别人 ...

  3. 塞翁失马,焉知非福:由 Styles.Render 所引发 runAllManagedModulesForAllRequests="true" 的思考

    最近在使用 MVC 开发的时候,遇到一个对我来说"奇怪的问题",就是使用 BundleTable 进行 CSS.JS 文件绑定,然后使用 Styles.Render.Scripts ...

  4. ReactJS分析之入口函数render

    前言 在使用React进行构建应用时,我们总会有一个步骤将组建或者虚拟DOM元素渲染到真实的DOM上,将任务交给浏览器,进而进行layout和paint等步骤,这个函数就是React.render() ...

  5. Cesium原理篇:6 Render模块(3: Shader)

    在介绍Renderer的第一篇,我就提到WebGL1.0对应的是OpenGL ES2.0,也就是可编程渲染管线.之所以单独强调这一点,算是为本篇埋下一个伏笔.通过前两篇,我们介绍了VBO和Textur ...

  6. Cesium原理篇:6 Render模块(4: FBO)

    Cesium不仅仅提供了FBO,也就是Framebuffer类,而且整个渲染过程都是在FBO中进行的.FBO,中文就是帧缓冲区,通常都属于高级用法,但其实,如果你了解了它的基本原理后,用起来还是很简单 ...

  7. Cesium原理篇:6 Render模块(5: VAO&RenderState&Command)

    VAO VAO(Vertext Array Object),中文是顶点数组对象.之前在<Buffer>一文中,我们介绍了Cesium如何创建VBO的过程,而VAO可以简单的认为是基于VBO ...

  8. render :template 和 render :parital

    1 .这两个都可以在controller和view中使用,而且好像可以替换,只是用:template,rails不会自动加下划线,用:partial,rails会自动添加下划线.而且规范的做法,:te ...

  9. AngularJs中,如何在render完成之后,执行Js脚本

    AngularJs是Google开源的前端JS框架.使用AngularJs, 我们能够容易地.健壮的开发出类似于Gmail一样的单页Web应用.AngularJs这个新兴的MVC前端框架,具有以下特点 ...

随机推荐

  1. window 安装 Protobuf

    环境安装 1:下载CMake 2:打开VS Command Prompt 3:修改工作目录到目标目录 cd C:\Path\to 4:创建编译完后 protobuf headers/libraries ...

  2. 读取Excel异常定义了过多字段的解决方法

    /// <summary> /// 从Excel文件导入数据 /// </summary> /// <param name="ExcelStr"> ...

  3. c语言学习之基础知识点介绍(十):数组

    本节主要介绍数组. 一.数组 /* 数组:一个变量可以存n个变量. 语法:类型 数组名[长度(正整数)]; 例如:int score[5];//定义了一个int类型的数组,长度为5,可以保存5个数据. ...

  4. AngularJS track by $index引起的思考

    今天写了一段程序,只是一个简答的table数据绑定,但是绑定select的数据之后,发现ng-change事件失去了效果,不知道什么原因. 主要用到的代码如下: <div id="ri ...

  5. html-----004

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. jQuery 遍历祖先

    祖先是父.祖父或曾祖父等等. 通过 jQuery,您能够向上遍历 DOM 树,以查找元素的祖先. 向上遍历 DOM 树 这些 jQuery 方法很有用,它们用于向上遍历 DOM 树: parent() ...

  7. Observer 模式

    Observer模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变.最常见的一个例子就是:对同一组数据进行 ...

  8. Ubuntu 下 安装QQ 截图工具

    1.由于ubuntu下是没有dll动态链接库的,所以需要安装一个软件wine,有这个东西之后,以后在ubuntu下就可以运行exe文件了.(wine是一款优秀的Linux系统平台下的模拟器软件,用来将 ...

  9. dotnet core开发体验之开始MVC

    开始 在上一篇文章:dotnet core多平台开发体验 ,体验了一把dotnet core 之后,现在想对之前做的例子进行改造,想看看加上mvc框架是一种什么样的体验,于是我就要开始诞生今天的这篇文 ...

  10. 结合rpyc使用python实现动态升级的方法

    动态升级,就是程序不退出的情况下,将其代码更新的策略.假设集群含有多个机器,然后每个机器部署一套程序,当升级的时候就要去所有的上面部署一把. (1)有个包装程序专门负责接口并检查是否需要更新,当需要更 ...