当前位置: 首页 > news >正文

【Unity Shader】Unity中如何创建Cubemap?

这篇博客其实是下一篇——Cubemap实现天空盒和环境映射,博客的小插曲,因为涉及到Cubemap的创建还是有很多门道可以说的,所以把它单独领出来放到了单独的一篇博客里。


创建Cubemap的方法有很多种,《入门精要》提到了三种,我这里就只介绍现在最常用的两种方法:直接由现成的纹理创建和脚本创建。

1 直接由现成的纹理创建

这一个方法最简单,同时这也是PBS(基于物理的渲染)常用的生成高质量Cubemap的方式。一般都是直接找一张现成的HDR图(推荐网站Poly Haven,可以免费下载HDRI),将下载的HDRI导入Unity就行。例如我在上述网站点开了一张HDRI图下载成.exr格式后,导入Unity:

Txture Shape选择Cube(不同版本设置成Cubemap的方式都不同)

​预览窗口就会变成如下的球体预览:

​这是我在场景中放一个球,并挂上一个可以反射纹理光的shader(这个shader在后续的博客中会会实现,这里就不再进行展示了,只是用作查看cubemap的效果),就得到了如下效果: 

​2 由脚本创建

上述的方法1是直接使用准备好的HDRI,因此得到的Cubemap会直接被场景中的所有物体公用。怎么理解这个公用?看下图就知道了!

​我在场景中放了三个球,结果三个球的反射效果都一模一样!这也太不符合实际了。

因此如果想实现——物体在场景中的不同位置,会生成对应的、属于自己的Cubemap,就要上Unity的脚本了。

2.1 创建脚本

我们在Assets -> Editor路径下创建一个C#脚本文件,需注意:名称一定要和脚本代码类名一致哦!例如我的脚本就跟官方一致:

脚本可以直接在Unity官方文档中获取:Unity - Scripting API: Camera.RenderToCubemap (unity3d.com)

 脚本主体为:

using UnityEngine;
using UnityEditor;
using System.Collections;

public class RenderCubemapWizard : ScriptableWizard
{
    public Transform renderFromPosition;
    public Cubemap cubemap;

    void OnWizardUpdate()
    {
        string helpString = "Select transform to render from and cubemap to render into";
        bool isValid = (renderFromPosition != null) && (cubemap != null);
    }

    void OnWizardCreate()
    {
        // create temporary camera for rendering
        GameObject go = new GameObject("CubemapCamera");
        go.AddComponent<Camera>();
        // place it on the object
        go.transform.position = renderFromPosition.position;
        go.transform.rotation = Quaternion.identity;
        // render into cubemap
        go.GetComponent<Camera>().RenderToCubemap(cubemap);

        // destroy temporary camera
        DestroyImmediate(go);
    }

    [MenuItem("GameObject/Render into Cubemap")]
    static void RenderCubemap()
    {
        ScriptableWizard.DisplayWizard<RenderCubemapWizard>(
            "Render cubemap", "Render!");
    }
}

其中的主要部分都是通过Camera.RenderToCubemap函数来实现的,这个函数是Unity内置的实现自定义位置对应Cubemap的函数。这个函数主要是干嘛呢?从代码注释可以了解到,其实就是在对应位置创建一个暂时性的camera,把在当前位置camera观察到的图像渲染到cubemap中,渲染完了之后再destroy掉这个camera。这样就能实现“物体在场景中的不同位置,会生成对应的、属于自己的Cubemap”了!

最后一个[MenuItem]步骤是提供给我们在Unity界面实现的,用了它就可以在GameObject -> Render into Cubemap打开界面进行下面的步骤。 

2.2 创建Cubemap纹理

按路径Create -> Legacy -> Cubemap创建一个新的Cubemap立方体纹理,这一步似乎只能按照传入6张纹理的方式传入Cubemap纹理。我这里传入的是《入门精要》提供的6张纹理图。

看一下底下这些参数的意思,

  • Face size——值越大,渲染的纹理分辨率越大,效果更好但占用内存也大
  • Mipmaps——勾选了之后,就可以预览到每一个Mip1-7层级

  • Readable——保证当前Cubemap是可以让脚本顺利将图片渲染进纹理的

2.3 实现Cubemap

沿Gameobject -> Render into Cubemap打开实现Cubemap窗口:

 第一个框框放入一个场景中创建的空GameObject,第二个框框放入之前创建的Cubemap,点Render!就能创建当前GameObject所处位置的专属立方体纹理了。

2.4 如何处理多个物体?

在第2章开头,我们就以三个不同位置的球却有相同反射情况这个不符合实际的效果,引出由脚本实现Cubemap的方法。那么问题来了,我在场景中创建两个小球,如果都是用同一个Cubemap,结果还会像第1章一样,二者反射情况一模一样:

​目前我认为可行的方法是每个需要计算反射光照的物体都创建一个属于它的Cubemap,每一个Cubemap都跟它对应的GameObject一起bake一次,可以发现这样做确实两个球的效果,包括Cubemap也都不一样了:

​观察下图效果可以发现,两个小球反映出的环境效果已经不同了!说明确实是根据当前的GameObject所在位置创建的Cubemap。

2.5 反射其实依旧是“假的”

但其实这样做反射依旧不是真实的!

这个“假的”是指——当前Cubemap是某一个特定位置bake出来的,并非实时的,如果我们把上述场景中的某一个小球挪动位置,如果不再次进行bake,小球反射出的环境并不会根据小球的位置进行相应的改变。

所以,我们在用Cubemap的时候还是老老实实选择考虑环境映射的效果,周围物体的还是交给间接光计算去处理~

相关文章:

  • 语言的未来:深度学习在自然语言处理中的革命
  • 关于提高自己技术能力的几点思考
  • 【智能算法】CEC2017测试集
  • 基于springboot+vue+Mysql的论坛管理系统
  • mysql数据库表的数据显示到前端tableView
  • 2.8 构建gradle环境
  • 【前端的讲解】
  • 也谈AIGC和ChatGPT的区别?
  • 预测淡水质量
  • 【风格迁移】CAST:对比学习,从图像特征而非其二阶统计量(Gram矩阵)中学习风格
  • 正则表达式中的特殊字符
  • spark基础
  • 面试百问:项目上线后才发现bug怎么办?
  • C语言《文件版本通讯录》
  • 【无人机】基于EKF、UKF、PF、改进PF滤波算法的无人机航迹预测(Matlab代码实现)
  • 一篇文章让你搞懂Java中的静态代理和动态代理
  • ROS1云课-导航实践测评
  • React中路由的参数传递 - 路由的配置文件
  • (附源码)计算机毕业设计SSM基于web的健康饮食信息管理系统
  • 算法的意义、如何学习算法和算法的复杂度
  • 由浅到深带你详谈Java实现数组扩容的三种方式【建议收藏】
  • 【ML on Kubernetes】第 1 章:机器学习的挑战
  • 技巧分享-电脑版微信如何登录多个账号
  • 精品微信小程序springboot居家养老服务+后台管理前后分离
  • GBase 8c 系统表之DB4AI.SNAPSHOT
  • 高频故障-文件扩展名消失(windows)
  • 冲突域与广播域(详解 + 区别)
  • 国庆征文获奖名单公布
  • java毕业设计大学生创新创业项目管理Mybatis+系统+数据库+调试部署
  • Mysql在可重复读事务隔离级别下怎么解决幻读的
  • 【数据结构Java版】链表之单链表的实现
  • C++的RAII思想以及在智能指针上的应用