24.动态生成立方体纹理

24.高级纹理-立方体纹理-动态生成立方体纹理


24.1 知识点

为什么要动态生成立方体纹理

在之前的学习中,我们了解到立方体纹理的一个重要用途就是环境映射。在实现反射、折射等效果时,立方体纹理是不可或缺的。它由六张 2D 纹理图片组成,能够表示不同方向的场景图像。对于天空盒等场景元素,通常这六张 2D 纹理图片是事先制作好的,作为全局资源共享。

然而,当使用这种预先制作的立方体纹理来实现反射、折射等效果时,可能会存在不够理想的情况。因为不同物体在场景中的位置不同,它们所需要的反射或折射效果也各自不同。例如,在一个场景中,立方体和球体应该能互相反射,才显得更加真实。

因此,为了更好地表现这种效果,建议为场景中的不同位置的物体动态生成立方体纹理,确保反射和折射效果与物体的实际位置相匹配。

如何动态生成立方体纹理

要动态生成立方体纹理,我们将结合以下技术:

  • Unity编辑器拓展:创建自定义编辑器窗口,便于操作。
  • Camera中的 RenderToCubemap 方法:利用该方法将摄像机的视图渲染到立方体纹理的六个面上。

对于一些场景展示类的项目,我们通常只需要在编辑器模式下生成一次纹理,而不需要每次运行时都生成。主要功能包括:

  • 自定义编辑器窗口,允许用户指定位置对象和Cubemap变量。
  • 自定义窗口中有一个生成按钮,点击后使用Camera中的RenderToCubemap自动生成对应的6张2D纹理贴图

动态生成立方体纹理

主要步骤

按照前述的思路,我们可以实现以下步骤来动态生成立方体纹理:

  1. 新建一个脚本 RenderToCubemap,并将其放置在 Editor 文件夹中。
  2. 让该脚本继承 EditorWindow 类,作为一个自定义的编辑器窗口。
  3. 实现一个静态方法,打开该窗口。
  4. OnGUI 中实现窗口布局,包含以下控件:
    • 关联位置对象的控件。
    • 关联立方体纹理的控件。
    • 生成按钮。
  5. 实现具体逻辑

注意事项:

  • 我们通过动态创建一个空物体为它添加摄像机组件的形式生成立方体纹理贴图,该对象为临时对象,使用完毕后即时删除即可
  • Cubemap 需要勾选 Readable
  • 立方体纹理的分辨率决定了生成纹理的清晰度,分辨率越高,纹理占用的内存越大。

新建一个脚本 RenderToCubemap 放在Editor文件夹中,让该类继承EditorWindow 将其作为一个编辑器窗口类,实现打开该窗口的静态函数

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class Lesson24_RenderToCubeMap : EditorWindow
{
    [MenuItem("立方体纹理动态生成/打开生成窗口")]
    static void OpenWindow()
    {
        Lesson24_RenderToCubeMap window = EditorWindow.GetWindow<Lesson24_RenderToCubeMap>("立方体纹理生成窗口");
        window.Show();
    }
}

创建一个CubeMap,cubemap上需要勾选Readable,分辨率决定了清晰度,改大了的话占的内存也会变大



实现OnGUI中的窗口布局,包括关联位置对象的控件、关联立方体纹理贴图的空间以及生成按钮

private GameObject gameObj;
private Cubemap cubeMap;

[MenuItem("立方体纹理动态生成/打开生成窗口")]
static void OpenWindow()
{
    //...
}

private void OnGUI()
{
    GUILayout.Label("关联对应位置对象");
    //用于关联对象的控件
    gameObj = EditorGUILayout.ObjectField(gameObj, typeof(GameObject), true) as GameObject;
    
    GUILayout.Label("关联对应立方体纹理");
    //用于关联立方体纹理的控件
    cubeMap = EditorGUILayout.ObjectField(cubeMap, typeof(Cubemap), false) as Cubemap;
    
    //点击按钮后 就去执行生成逻辑
    if (GUILayout.Button("生成立方体纹理"))
    {

    }
}

点击按钮先对位置对象和立方体纹理贴图进行判空,空弹出提示。生成临时对象,把位置设置的和关联对象一直,给临时对象添加摄像机组件,调用摄像机的RenderToCubemap方法生成6张2D纹理贴图,用于立方体纹理,用完了立刻删除tmpObj

//点击按钮后 就去执行生成逻辑
if (GUILayout.Button("生成立方体纹理"))
{
    //判空返回
    if (gameObj == null || cubeMap == null)
    {
        EditorUtility.DisplayDialog("提醒", "请先关联对应对象和立方体贴图", "确认");
        return;
    }

    //动态的生成立方体纹理
    //生成临时对象
    GameObject tempGameObj = new GameObject("临时对象");
    tempGameObj.transform.position = gameObj.transform.position;
    //添加摄像机组件
    Camera camera = tempGameObj.AddComponent<Camera>();
    //关键方法RenderToCubemap,可以马上生成6张2D纹理贴图 用于立方体纹理
    camera.RenderToCubemap(cubeMap);
    //用完了立刻删除tmpObj
    DestroyImmediate(tempGameObj);
}


分别创建Cubemap材质,设置给球和立方体,查看动态纹理效果



在运行时动态生成立方体纹理

除了编辑器模式下生成立方体纹理外,RenderToCubemap 方法也可以在运行时实时生成立方体纹理。然而,这种方式可能会对性能产生影响,尤其是在频繁调用时。为了优化性能,可以采取以下措施:

  • LateUpdate 中调用:这样可以确保在场景渲染完毕后再生成纹理,避免对渲染帧率的影响。
  • 降低纹理分辨率:降低生成的立方体纹理的分辨率,可以减少内存占用和计算开销。
  • 分帧渲染:使用 RenderToCubemap 的重载方法,可以逐个面渲染立方体纹理,避免一次性渲染所有六个面。
  • 减少更新频率:避免每帧都执行该操作,可以设定一定的时间间隔或条件来触发更新。

通过这些优化措施,可以有效降低动态生成立方体纹理对性能的影响。


24.2 知识点代码

Lesson24_高级纹理_立方体纹理_动态生成立方体纹理.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson24_高级纹理_立方体纹理_动态生成立方体纹理 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 为什么要动态生成立方体纹理?

        //我们之前学习的知识中提到过
        //立方体纹理的其中一个最大的作用就是环境映射
        //在实现反射、折射等等效果时,需要用到立方体纹理来制作对应效果
        //而立方体纹理中最重要的就是组成它的6张2D纹理图片

        //对于之前学过的天空盒来说,6张2D纹理图片可以根据想要的美术表现效果来进行自定义制作
        //提前把纹理制作好,直接使用即可
        //这种立方体纹理往往是被提前做好的,是场景中物体们共用的

        //但如果制作反射、折射等效果还是使用这样的立方体纹理,效果肯定不够理想
        //因为物体在场景上的位置不同,产生的对应效果也会是不同的
        //举例说明

        //因此为了更好更真实的表现效果,对于场景中不同位置的物体
        //我们应该为它们在不同位置生成不同的立方体纹理(6张2D纹理贴图)

        #endregion

        #region 知识点二 如何动态生成立方体纹理?

        //我们将结合
        //1.Unity编辑器拓展
        //2.Camera中的RenderToCubemap方法
        //这两个知识点,在对应位置生成对应的立方体纹理贴图
        //对于一些场景展示类项目,我们不需要实时生成,只需要在编辑器模式下生成一次即可
        //主要要完成的功能为:
        //1.自定义编辑器窗口,关联对象(通过对象来指定位置)和cubemap变量
        //2.自定义窗口中有一个生成按钮,点击后使用Camera中的RenderToCubemap自动生成对应的6张2D纹理贴图

        //其中编辑器窗口相关功能使用Unity编辑器拓展相关知识
        //自动生成立方体纹理贴图功能使用Camera中的RenderToCubemap方法
        //该方法可以将任意位置观察到的场景图像存储到6张图像中

        #endregion

        #region 知识点三 动态生成立方体纹理

        //按照知识点二中的制作思路来实现逻辑
        //1.新建一个脚本 RenderToCubemap 放在Editor文件夹中
        //2.让该类继承EditorWindow 将其作为一个编辑器窗口类
        //3.实现打开该窗口的静态函数
        //4.实现OnGUI中的窗口布局,添加以下控件
        //  4-1:关联位置对象的控件
        //  4-2:关联立方体纹理贴图的空间
        //  4-3.生成按钮
        //5.实现具体逻辑
        //  注意点:
        //  1.我们通过动态创建一个空物体为它添加摄像机组件的形式生成立方体纹理贴图
        //    该对象为临时对象,使用完毕后即时删除即可
        //  2.cubemap上需要勾选Readable
        //  3.分辨率决定了清晰度

        #endregion

        #region 知识点四 在运行时动态生成

        //Camera中的RenderToCubemap也可以在运行时实时动态生成立方体纹理
        //但是要注意对性能的影响
        //1.在LateUpdate中使用
        //2.降低立方体纹理贴图的分辨率
        //3.分帧渲染,RenderToCubemap有重载,可以一个面一个面的渲染
        //4.降低更新频率,不要每帧执行
        //等等

        #endregion
    }
}

Lesson24_RenderToCubeMap.cs

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class Lesson24_RenderToCubeMap : EditorWindow
{
    private GameObject gameObj;
    private Cubemap cubeMap;

    [MenuItem("立方体纹理动态生成/打开生成窗口")]
    static void OpenWindow()
    {
        Lesson24_RenderToCubeMap window = EditorWindow.GetWindow<Lesson24_RenderToCubeMap>("立方体纹理生成窗口");
        window.Show();
    }

    private void OnGUI()
    {
        GUILayout.Label("关联对应位置对象");
        //用于关联对象的控件
        gameObj = EditorGUILayout.ObjectField(gameObj, typeof(GameObject), true) as GameObject;
        
        GUILayout.Label("关联对应立方体纹理");
        //用于关联立方体纹理的控件
        cubeMap = EditorGUILayout.ObjectField(cubeMap, typeof(Cubemap), false) as Cubemap;
        
        //点击按钮后 就去执行生成逻辑
        if (GUILayout.Button("生成立方体纹理"))
        {
            //判空返回
            if (gameObj == null || cubeMap == null)
            {
                EditorUtility.DisplayDialog("提醒", "请先关联对应对象和立方体贴图", "确认");
                return;
            }

            //动态的生成立方体纹理
            //生成临时对象
            GameObject tempGameObj = new GameObject("临时对象");
            tempGameObj.transform.position = gameObj.transform.position;
            //添加摄像机组件
            Camera camera = tempGameObj.AddComponent<Camera>();
            //关键方法RenderToCubemap,可以马上生成6张2D纹理贴图 用于立方体纹理
            camera.RenderToCubemap(cubeMap);
            //用完了立刻删除tmpObj
            DestroyImmediate(tempGameObj);
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏