[Unity3D] 动态立方体贴图系统

server/2025/3/12 9:25:34/


动态立方体贴图系统

介绍

1、准备

2、实现

3、原理

4、另请参阅



动态立方体贴图系统

        当今,反射是可以让一个 Shader 在感官上具备视觉冲击力的一项关键技术,它是在 Shader 表面上模拟环境反射的一个过程,在这个过程中使用了你周围的世界(环境)信息,并且让 Shader 去反射这些世界(环境)的信息。因为在这部分中我们需要使用一种称为 Cubemap 的新型纹理。这种类型的纹理由 6 张纹理组成,并且用这 6 张纹理以立方体的方式围绕当前表面。所以想象一个立方体,立方体的6个面上被赋予了这6张纹理。这将允许我们去捕获周围环境并将其烘焙到纹理中。

介绍

        当物体在环境中移动时,我们的反射并不能真正的反射出真实的世界。例如,如果你有一个由多个房间和走廊组成的环境,我们无法为整个关卡烘焙一个立方体贴图,并将其放在单个立方体贴图中。这并不能反射出房间之间合理的环境。我们会得到一个非常静态、无趣的反射。

        有几种方法可以解决这个问题,使一个房间的反射与第二个房间的反射不同。第一种也是最基本的方法是根据 Room 中的位置交换 Cubemap。因此,当您从一个房间移动到另一个房间时,立方体贴图将替换为该房间正确的立方体贴图。第二种方法是角色在环境中移动时,实时更新 Cubemap,最终在游戏进行的每一帧中都会获得一个新的 Cubemap。虽然第二个选项听起来在视觉上更吸引人,因为你会看到立方体贴图之间出现一个短暂切换的一个动作,但它相当昂贵,非常占资源,因此需要与你的游戏所需所有其他资源进行权衡。

        在本小节中我们介绍第一个选项,并向你展示如何设置一个非常简单的系统,以便根据环境中基于位置信息来切换两个立方体贴图。本小节的最后一部分提供了有关创建实时反射系统的更多信息,因此,如果你有兴趣并想了解这两种技术之间的区别,那么就可以开始了!

1、准备

  • 1. 我们需要创建一个新场景,并在世界中放置一个地平面和一个球体。此外,添加一个定向光源,为我们的着色器获取一些光照。
  • 2. 继续向场景添加两个空的 GameObject 构造函数,并分别将它们命名为 pos001 和 pos002。
  • 3. 然后,让我们为球体赋予一个新的材质球,并将我们刚刚在上一小节中创建的菲涅尔着色器赋予到我们新材质球上。你的场景现在应类似于如 图6.1 。
  • 4. 最后,让我们创建一个脚本并将其命名为 SwapCubemaps.cs。

        以下屏幕截图显示了我们准备好的场景的结果,该场景已准备好用于我们的动态反射系统:

图6.1

2、实现

        场景准备就绪后,我们可以按照接下来的几个步骤开始编写反射系统代码。

  • 步骤1. 让我们在声明类之前添加 [ExecuteInEditMode]。
[ExecuteInEditMode]
public class SwapCubemaps : MonoBehaviour
{
  • 步骤2. 然后,我们需要声明一些变量来存储我们系统中的所有数据。我们将在本小节中的下一部分解释这些。
public Cubemap cubeA;
public Cubemap cubeB;public Transform posA;
public Transform posB;private Material curMat;
private Cubemap curCube;
  • 步骤3. 为了直观地看到立方体贴图位置在空间中的位置,我们需要利用 Unity3D 为我们提供的超棒的 Gizmos 功能。因此,让我们将以下代码添加到脚本的底部:
void OnDrawGizmos()
{Gizmos.color = Color.green;if(posA){Gizmos.DrawWireSphere(posA.position, 0.5f);}if(posB){Gizmos.DrawWireSphere(posB.position, 0.5f);}
}
  •  步骤4. 现在,我们需要创建一个新函数,该函数将根据我们设置的每个位置之间的距离来确定我们应该使用哪个立方体贴图:
private Cubemap CheckProbeDistance()
{float distA = Vector3.Distance(transform.position, posA.position);float distB = Vector3.Distance(transform.position, posB.position);if(distA < distB){return cubeA;}else if(distB < distA){return cubeB;}else{return cubeA;}}
  •  步骤5. 最后,我们只需要检查每一帧,看看环境中每个位置之间的距离是多少,并在我们的材质中合适的时候进行交换立方体贴图:
// Update is called once per frame
void Update()
{//curMat = renderer.sharedMaterial;curMat = GetComponent<Renderer>().sharedMaterial;if(curMat){curCube = CheckProbeDistance();curMat.SetTexture("_Cubemap", curCube);}}
  • 完整代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;[ExecuteInEditMode]
public class SwapCubemaps : MonoBehaviour
{public Cubemap cubeA;public Cubemap cubeB;public Transform posA;public Transform posB;private Material curMat;private Cubemap curCube;void OnDrawGizmos(){Gizmos.color = Color.green;if(posA){Gizmos.DrawWireSphere(posA.position, 0.5f);}if(posB){Gizmos.DrawWireSphere(posB.position, 0.5f);}}private Cubemap CheckProbeDistance(){float distA = Vector3.Distance(transform.position, posA.position);float distB = Vector3.Distance(transform.position, posB.position);if(distA < distB){return cubeA;}else if(distB < distA){return cubeB;}else{return cubeA;}}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){//curMat = renderer.sharedMaterial;curMat = GetComponent<Renderer>().sharedMaterial;if(curMat){curCube = CheckProbeDistance();curMat.SetTexture("_Cubemap", curCube);}}
}

         保存着色器后,返回到 Unity 编辑器中等待着色器编译好后。点击 Play 并来回移动球体。您应该会看到类似于下 图6.2 的结果:

图6.2

3、原理

        我们只需通过声明类的 [ExecuteInEditMode] 属性来启动此脚本。这会告诉 Unity,我们希望在编辑器中运行脚本来切换立方体贴图,而不仅仅是在点击 Play 时运行。这将允许我们进行测试立方体贴图的切换,而无需点击 Play — 这样会将工作流程变得更快捷。

        然后,该脚本包含一些变量,我们使用这些变量允许使用者输入两个立方体贴图和两个位置信息,我们使用它们来比较距离。最后,我们有两个私有变量,当程序运行时,我们可以使用它们来跟踪当前材质球和立方体贴图。

        有了变量,我们就可以使用 OnDrawGizmos() 内置函数来实际显示我们让用户输入的转换位置的信息。这些位置将命令脚本何时切换我们的立方体贴图。

        然后我们来了解这个程序的真正内容。我们声明自己的函数/方法,该函数/方法将使用 Vector3.Distance() 计算球体与两个变换中任何一个的距离。然后,它会检查哪个距离更小,并返回该位置的 Cubemap。

        最后,在 Update() 函数中,我们从球体获取当前的材质球,或者此脚本附加到的对象,然后简单地分配从自定义函数返回的当前选定的立方体贴图。

        这只是一个非常简单的脚本来阐述这个概念,但它可以扩展成一个完整的系统,每个房间都有多个立方体贴图。该系统可以在运行时为我们自动生成所有的立方体贴图,这对于无法负担完全实时反射系统的游戏来说非常有用。

4、另请参阅

        您还可以尝试创建实时反射系统,其中 Cubemap 会针对游戏中的每一帧进行更新。这绝对是一个视觉上更吸引人的系统,但确实会以性能为代价:

Unity - Scripting API: Camera.RenderToCubemaphttp://docs.unity3d.com/Documentation/ScriptReference/Camera.RenderToCubemap.htmlhttp://docs.unity3d.com/Documentation/ScriptReference/Camera.RenderToCubemap.html


摘自Unity Shaders and Effets Cookbook - 第四章:Reflecting Your World - 第6节 

链接如下:

第四章:反射-Reflecting Your World《Unity Shaders and Effets Cookbook》-CSDN博客https://blog.csdn.net/m0_56734636/article/details/145396167

 


http://www.ppmy.cn/server/174413.html

相关文章

React 性能优化

从 React 层面上&#xff0c;可以进行以下性能优化: 1.使用 memoization(记忆化):通过使用 React.memo()或useMemo()来避免不必要的重新渲染。这对于纯函数组件和大型组件特别有用。 2.使用 shouldComponentUpdate 或PureComponent:在类组件中&#xff0c;可以通过重写shouldCo…

使用pytest-xdist让自动化并行测试变得轻松简单

关注开源优测不迷路 大数据测试过程、策略及挑战 测试框架原理&#xff0c;构建成功的基石 在自动化测试工作之前&#xff0c;你应该知道的10条建议 在自动化测试中&#xff0c;重要的不是工具 在实际的项目中即便没有数千个测试&#xff0c;也有 100 个测试。当你运行 pytest …

Go语言单元测试和基准测试

单元测试和基准测试 在Go语言中&#xff0c;单元测试和基准测试是两种不同类型的测试&#xff0c;它们的目的、使用场景以及实现方式都有所不同。以下是它们的主要区别&#xff1a; 1. 目的 单元测试&#xff1a; 目的是验证代码单元&#xff08;通常是函数或方法&#xff09…

卡顿优化(matrix与blockcanary)

卡顿优化(matrix与blockcanary) 在Android应用开发中,流畅的用户体验是产品成功的关键因素之一。然而,随着应用功能的不断丰富,卡顿问题也随之而来。本文将深入探讨Android应用中的卡顿问题,包括卡顿的原因、检测方法以及优化策略,并重点介绍两款强大的卡顿检测工具:M…

SQL语句执行顺序是什么?

理解SQL语句的执行顺序对于优化查询和提高数据库性能非常重要。 1. SQL语句的执行过程 当我们执行一条SQL语句时&#xff0c;MySQL会按照一定的顺序解析和执行这条语句。这个过程可以分为以下几个阶段&#xff1a; 1.1 解析SQL语句 MySQL首先会解析SQL语句&#xff0c;将其分…

Flutter 学习之旅 之 flutter 不使用插件,实现简单带加载动画的 LoadingToast 功能

Flutter 学习之旅 之 flutter 不使用插件&#xff0c;实现简单带加载动画的 LoadingToast 功能 目录 Flutter 学习之旅 之 flutter 不使用插件&#xff0c;实现简单带加载动画的 LoadingToast 功能 一、简单介绍 二、LoadingToast 三、简单案例实现 四、关键代码 一、简单…

Spring Boot 与 Spring MVC 有何不同

Spring Boot 和 Spring MVC 都是 Spring 框架的一部分&#xff0c;但它们有不同的目标和功能。以下是它们之间的主要区别&#xff1a; 1. 核心目标 Spring Boot&#xff1a;Spring Boot 的目标是简化 Spring 应用的配置和部署。它通过提供默认配置和嵌入式服务器(如 Tomcat、Je…

【Java开发指南 | 第三十四篇】IDEA没有Java Enterprise——解决方法

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 1、新建Java项目2、单击项目名&#xff0c;并连续按两次shift键3、在搜索栏搜索"添加框架支持"4、勾选Web应用程序5、最终界面6、添加Tomcat 1、新建Java项目 2、单击项目名&#xff0c;并连续按两次…