【Unity Shader入门精要 第10章】高级纹理(一)

1. 立方体纹理原理

立方体纹理由6张图片组成,每张图片分别对应立方体的一个面。这6张图片代表沿世界空间下的轴线(上下左右前后)观察所得的图像

立方体的应用主要分为两类:

  • 单纯利用6张图片的展示功能,为我们提供一个环境背景,比如天空盒
  • 对立方体纹理进行采样,显示在物体表面,反映物体对周围环境的反射、折射等效果,也就是用于环境映射

与2D纹理采样不同,立方体纹理采样时,需要提供一个3维的uv坐标,该坐标提供的是一个世界空间的方向,从立方体的中心出发,沿这个方向前进,会与6张图片中某一张相交在一点,这一点的颜色值即为采样结果。

在这里插入图片描述

2. 用于天空盒

创建天空盒

  • 先创建一个材质,本次使用的材质命名为Chapter_10_SkyBox_Mat
  • 为材质选择天空盒Shader,如下图有四种类型的天空盒Shader
    在这里插入图片描述
  • 6-Sided
    • 使用6张纹理作为天空盒的6个面
    • TintColor——调整整体颜色
    • Exposure——调整亮度
    • Rotation——调整Y轴旋转

(没有找到合适的资源,不试了)
在这里插入图片描述

  • Cubemap

    • 通过一张Cube纹理生成天空盒
      在这里插入图片描述
    • Cube纹理如下
      在这里插入图片描述
  • Panoramic

    • 由美术提供的一张特殊制作的高清纹理生成天空盒
      在这里插入图片描述

设置天空盒

  • 通过Window → Rendering → Lighting 打开光照设置面板
  • 在Environment标签页下设置当前场景天空盒
    在这里插入图片描述
  • 摄像机的Clear Flags 选项选择为 Skybox
  • 也可以为摄像机添加skybox组件,为组件选择其他的天空盒,此时当前摄像机在渲染时就会使用组件指定的天空盒替换掉光照面板中设置的场景天空盒
    在这里插入图片描述
    在这里插入图片描述

渲染顺序

在这里插入图片描述
从上面的截图中可以看到,天空盒的渲染序列为1000(Background)

在【Unity Shader入门精要 第8章】透明效果(一)中曾经说过,渲染队列为Background的物体用于远处的背景,但实际天空盒这些背景并不会真的最先渲染,而是在所有不透明的物体之后再进行渲染

这样在经过不透明物体的深度写入后,天空盒中的大量片元都不会通过深度测试,也就不需要进入片元着色器,可以有效降低 Over Draw

3. 用于环境映射

3.1 创建

除了用于天空盒,立方体纹理的另一个主要作用是用于环境映射。换句话说就是,以场景中的某一点为中心,通过立方体纹理反映从这一点看到的上下前后左右的环境,并可以通过对立方体纹理采样表现这一点的反射折射等现象。

可见,与天空盒不同,用于环境映射的立方体纹理的中心点通常是不确定的,其位置由使用立方体纹理的物体决定,需要表现哪个物体的反射等现象,就需要以该物体的位置为中心创建立方体纹理,这样才能正确表现该物体的周边环境信息。

在创建时,既可以提前准备好用于表示环境的纹理资源,也可以通过摄像机实时创建,常见有以下三种做法:

  • 先有图片,后生成资源——将美术提供提前绘制好的图片导入工程,将类型设置为Cube
    在这里插入图片描述
  • 先有资源,后提供图片——直接在资源面板里右键创建一个Cubemap资源,然后为该资源提供所需的6张图片
    在这里插入图片描述
    在这里插入图片描述
  • 脚本调用Camera的RenderToCubemap接口动态创建
GameObject _go = new GameObject();
_go.transform.position = TargetTransform.position;
Camera _tmpCam = _go.AddComponent<Camera>();
_tmpCam.RenderToCubemap(TargetCubemap);
DestroyImmediate(_tmpCam);

3.2 反射和折射

反射原理

在这里插入图片描述
光线与物体表面交互后改变传播方向进入我们的眼睛(摄像机),这个过程只改变光线的传播路径,不改变光线的颜色,因此我们就看到在物体表面倒映出周边环境的样子。

要模拟反射现象,只需要在渲染Pass中,计算出环境中的哪一点的光线在与当前片元交互后会传入摄像机,并根据反射的强度将这一点的颜色与物体本身颜色做融合即可。

因此,模拟反射的重点即是找到光线的来源。基于光路可逆的原理,我们可以从视线方向出发,经过与片元交互反向求出光线方向,然后用该方向对表示环境信息的立方体纹理进行采样。Unity中内置了 reflect 方法来快速计算模拟反射的光线来源方向。

o.worldRefl = reflect(-_worldView, o.worldNormal);

其中:

  • 第一个参数为视线方向,并且是由摄像机指向物体的方向,而Unity中内置变量或内置方法返回的观察方向都是从物体指向摄像机的,因此在使用时需要先取反
  • 第二个参数为当前处理的点的法线
  • 该方法中的参数都不需要归一化

折射原理

在这里插入图片描述
折射现象的原理为光线与物体交互后进入物体内部并发生方向改变,最终进入摄像机,光线改变的角度与光线前后所处介质的折射率有关:
在这里插入图片描述
可见折射的角度与入射光线的角度成一定比例,我们可以用一个折射率系数 RefractRatio 来概括表示这个比例关系。跟反射一样,在模拟折射现象的时候,也可以从视线方向出发,根据折射率系数逆向求出入射光线方向,然后对表示环境信息的立方体纹理采样。

Unity同样提供了用于计算折射的方法

o.worldRefr = refract(-normalize(_worldView), normalize(o.worldNormal), _RefrcatRatio);

其中:

  • 第一个参数为视线方向,同样需要从摄像机指向交互点
  • 第二个参数为交互点的法线方向
  • 第三个参数为折射率系数
  • 与反射不同,在该方法中,传入的视线方向和法线方向都需要经过归一化处理

一个综合了反射和折射的测试Shader:

Shader "MyShader/Chapter_10/Chapter_10_ReflectAndRefract_Shader"
{
    Properties
    {
        _CubeMap ("Cubemap", Cube) = "_Skybox" {}
        _Color("Color", Color) = (1, 1, 1, 1)
        _RefrcatRatio("RefractRatio", Range(0.1, 1)) = 0.5
        _ReflToRefr("ReflToRefr", Range(0, 1)) = 0
        _Amount("Amount", Range(0, 1)) = 0
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode" = "ForwardBase" }
            
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float3 worldRefl : TEXCOORD2;
                float3 worldRefr : TEXCOORD3;
                SHADOW_COORDS(4)
            };
            
            samplerCUBE _CubeMap;
            fixed4 _Color;
            fixed _RefrcatRatio;
            fixed _ReflToRefr;
            fixed _Amount;
            
            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 _worldView = WorldSpaceViewDir(v.vertex);
                o.worldRefl = reflect(-_worldView, o.worldNormal);
                o.worldRefr = refract(-normalize(_worldView), normalize(o.worldNormal), _RefrcatRatio);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                TRANSFER_SHADOW(o);
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                
                float3 _worldNomal = normalize(i.worldNormal);
                float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 _diffuse = _LightColor0.rgb * _Color.xyz * saturate(dot(_worldNomal, _worldLight));
                
                fixed3 _reflColor = texCUBE(_CubeMap, i.worldRefl).rgb;
                fixed3 _refrColor = texCUBE(_CubeMap, i.worldRefr).rgb;
                fixed3 _sampler = lerp(_reflColor, _refrColor, _ReflToRefr);
                
                UNITY_LIGHT_ATTENUATION(_atten, i, i.worldPos);
                fixed3 _finalColor = _ambient + lerp(_diffuse, _sampler, _Amount) * _atten;
                
                return fixed4(_finalColor, 1);
            }
            
            ENDCG
        }
    }
}

效果如下:
在这里插入图片描述

在这里插入图片描述

3.3 菲涅尔反射

菲涅尔反射描述了一种光学现象——当光线照射到物体上时,一部分被反射,一部分进入物体内部,而被反射的光线与入射光存在一定的比率关系。

一个现实中的例子就是当我们看水面时会发现,近处的部分可以透过水面看到水底,而远处的部分就只能看到反射。

在菲涅尔反射现象中,反射光线与入射光线的比率可以通过菲涅尔等式获得。一个描述真实世界菲涅尔反射的等式是非常复杂的,在渲染中往往通过一些公式进行近似模拟,Schlick菲涅尔近似等式就是其中常用的一个等式:
FSchlick(v, n) = F0 + (1 - F0)(1 - v · n)5
其中 v为视线方向,n为法线方向,F0为反射系数,用于控制菲涅尔反射的强度。

一个加入了菲涅尔反射的测试Shader:

Shader "MyShader/Chapter_10/Chapter_10_Fresnel_Shader"
{
    Properties
    {
        _CubeMap ("Cubemap", Cube) = "_Skybox" {}
        _Color("Color", Color) = (1, 1, 1, 1)
        _FresnelScale("FresnelToRefl", Range(0, 1)) = 0
        _Pow("Pow", Range(1, 50)) = 5
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode" = "ForwardBase" }
            
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float3 worldRefl : TEXCOORD2;
                float3 worldView : TEXCOORD3;
                SHADOW_COORDS(4)
            };
            
            samplerCUBE _CubeMap;
            fixed4 _Color;
            fixed _FresnelScale;
            half _Pow;
            
            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldView = WorldSpaceViewDir(v.vertex);
                o.worldRefl = reflect(-o.worldView, o.worldNormal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                TRANSFER_SHADOW(o);
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                
                float3 _worldNomal = normalize(i.worldNormal);
                float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 _diffuse = _LightColor0.rgb * _Color.xyz * saturate(dot(_worldNomal, _worldLight));
                
                fixed3 _reflColor = texCUBE(_CubeMap, i.worldRefl).rgb;
                
                float3 _worldView = normalize(i.worldView);
                fixed _fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(_worldView, _worldNomal), _Pow);
                
                UNITY_LIGHT_ATTENUATION(_atten, i, i.worldPos);
                fixed3 _finalColor = _ambient + lerp(_diffuse, _reflColor, saturate(_fresnel)) * _atten;
                
                return fixed4(_finalColor, 1);
            }
            
            ENDCG
        }
    }
}

效果如下:
在这里插入图片描述

另外,也可以利用这个现象做简单的外轮廓显示:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/676749.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎么下载 jar 包

一、在Maven仓库里面下载 Maven仓库 网址&#xff1a;https://mvnrepository.com/ 二、搜索需要的 jar 包&#xff08;以 druid 为例&#xff09; 三、找到 druid jar包&#xff0c;点进去 四、找到自己需要的版本&#xff0c;点进去 五、 点 jar 下载

数字化前沿:Web3如何引领未来技术演进

在当今数字化时代&#xff0c;随着技术的不断发展和创新&#xff0c;Web3作为一种新兴的互联网范式&#xff0c;正逐渐成为数字化前沿的代表。Web3以其去中心化、加密安全的特性&#xff0c;正在引领着未来技术的演进&#xff0c;为全球范围内的科技创新带来了新的可能性和机遇…

第二讲笔记:隐私计算助力数据要素流通

1、数据要素流转与数据 2、数据外循环中的信任 焦虑 信任焦虑背后的代表性案例 内鬼门 &#xff1a; 2023 年 &#xff0c; 美国科技公司 Ubiquiti在2021年1月曝出数据泄露事 件&#xff0c; “攻击者”在随后的“谈判”中试 图向该企业勒索近200万美元&#xff08;50比特 币&…

.Net Core Console 项目如何使用 HttpClient 与 Web 服务通信

前言 HttpClient 类是在 .NET Framework 4.5 和 .NET Core 中引入的新的 HTTP 客户端类&#xff0c;是 .NET 用于发送和接收 HTTP 请求的类&#xff0c;相比之前的 WebRequest 和 HttpWebRequest&#xff0c; 它提供了现代的、易用的 API&#xff0c;并且具有更好的性能和扩展…

【Spring Cloud】微服务链路跟踪Sleuth

目录 为什么要使用微服务链路跟踪微服务的现状多服务协同工作复杂的调用链条容易出错 微服务链路跟踪需要实现的需求实现监控决策避免技术债务快速定位故障 微服务链路跟踪的技术要求低消耗应用透明延展性可控采样率可视化 Spring Cloud Sleuth简介Spring Cloud Sleuth的4个特点…

‘yarn’不是内部或外部命令,也不是可运行的程序或批处理文件。

目录 问题点 解决方式 # 安装 # 版本 # 本地发生变化&#xff08;了解&#xff09; # 安装项目依赖 新问题 解决方式 问题点 在vscode中&#xff0c;点击dev运行&#xff0c;项目报错【Q1】 * 正在执行任务: yarn run dev yarn : 无法将“yarn”项识别为 cmdlet、函数…

代码随想录算法训练营第26天(py)| 回溯 | 39. 组合总和、40.组合总和II、131.分割回文串

39. 组合总和 力扣链接 给定一个无重复元素的数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明&#xff1a; 所有数字&#xff08;包括 target&#xff09;都是正整数…

利用MaxKB+Ollama:搭建智能问答系统_Ubuntu部署maxkb

Docker方式&#xff0c;不建议使用 即使maxKB和ollama在同一目录下&#xff0c;API域名也显示无效。 Ollama下载网址&#xff1a;Download Ollama on Linux Linux下载&#xff1a;curl -fsSL https://ollama.com/install.sh | sh The Ollama API is now available at 127.0.…

PE文件结构详解之头信息解析

PE文件结构详解 一、前言1.概述2.PE文件结构3.所用工具 二、DOS头&#xff08;DOS Header&#xff09;解析1.作用2.图例3.参数详解4.总结 三、DOS Stub1.作用2.图例 四、NT头&#xff08;NT Header&#xff09;解析1.作用2.PE标识图例3.文件头&#xff08;COFF头&#xff09;图…

TinyMCE 富文本编辑器:打造个性化编辑体验

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 TinyMCE 富文本编辑器&#xff1a;打造个性化编辑体验 应用场景介绍 TinyMCE 是一款功能强大的富文本编辑器&#xff0c;广泛应用于网站内容管理、博客创作、在线文档编辑等场景。它提供了一系列丰富的编辑功…

LightDB pro*c迁移指南(游标模块)

文章目录 一、不使用SQLDA描述符范围的游标操作1.1 oracle 案例1.1.1 使用游标获取数据1.1.2 对于fetch结果集怎么去利用 1.2 LightDB 案例1.2.1 使用游标获取数据1.2.2 对于fetch结果集怎么去利用 3 总结&#xff1a;不同项 二、使用SQLDA描述符范围的游标操作2.1 Oracle样例2…

基于java的CRM客户关系管理系统(五)

目录 第五章 系统的详细设计与实现 5.1 持久层设计 5.1.1 创建关系映射 5.1.2 与数据库的连接 5.1.3 Hibernate的ORM映射 5.1.4 Struts的配置文件 5.1.5 Spring 的配置文件 5.1.6 DAO层设计 5.2 逻辑业务层设计 5.2.1 业务逻辑类的实现 前面内容请移步 基于java的C…

Jmeter干货分享:当你的Log viewer不显示日志时,可能是引入的Jar包冲突导致

问题描述 近期使用Jmeter时发现了一个非常奇怪的问题&#xff0c;就是Jmeter是可以正常使用运行脚本&#xff0c;但是在Log viewer中确没有任何日志&#xff0c;如下图&#xff1a; 问题排查过程 真是百思不得其解啊&#xff0c;在网上各种获取资料&#xff0c;大多数都是说跟…

001----flask

flask---001 flask与django对比今日概要问答今日详细1.flask快速使用1.2 快速使用flask1.3 用户名密码登录 flask与django对比 django是个大而全的框架&#xff0c;flask是一个轻量级的框架。 django内部为我们提供了非常多的组件&#xff1a;orm/session/cookie/admin/from/mo…

【学习】企业如何选择一个合适的DCMM咨询机构

DCMM是我国首个数据管理领域正式发布的国家标准。旨在帮助企业利用先进的数据管理理念和方法&#xff0c;建立和评价自身数据管理能力&#xff0c;持续完善数据管理组织、程序和制度&#xff0c;充分发挥数据在促进企业向信息化、数字化、智能化发展方面的价值。该标准借鉴了国…

Python学习从0开始——Kaggle机器学习003总结

Python学习从0开始——Kaggle机器学习003总结 一、加载及浏览数据二、机器学习模型三、模型验证四、欠拟合和过拟合五、随机森林 一、加载及浏览数据 # 路径 melbourne_file_path ../input/melbourne-housing-snapshot/melb_data.csv # 读取 melbourne_data pd.read_csv(mel…

为什么大家都要考CDA数据分析师认证

为什么学习数据分析&#xff1f; 2024年&#xff0c;是一个被数据影响的时代。数据&#xff0c;如同无形的燃料&#xff0c;驱动着现代社会的运转。从全球互联网的用户每天产生的2.5亿TB数据&#xff0c;到制造业的传感器、金融交易、医疗病历等领域的海量信息&#xff0c;数据…

排序算法——归并排序以及非递归实现

一、归并排序思想 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide andConquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列…

使用Python绘制瀑布图

使用Python绘制瀑布图 瀑布图效果代码 瀑布图 瀑布图&#xff08;Waterfall Chart&#xff09;是一种数据可视化工具&#xff0c;用于展示累积数值的变化&#xff0c;尤其适合于展示随时间或过程中的增减变化。它通常用于财务分析&#xff0c;如展示收入、支出和净利润的变化过…

分离式光电液位传感器与浮球开关相比具有哪些优势

分离式光电液位传感器与浮球开关相比有哪些优势&#xff1f;分离式光电液位传感器依据光学原理&#xff0c;在传统光学传感器的基础上进行了改进。其特点是将光学组件分离出来&#xff0c;置于水箱外部感应&#xff0c;而传感器本身则独立于水箱外。这种设计有效解决了浮球开关…