【Unity3D】反射和折射

news2025/1/16 16:39:20

1 前言

        立方体纹理(Cubemap)和天空盒子(Skybox)中介绍了生成立方体纹理和制作天空盒子的方法,本文将使用立方体纹理进行采样,实现反射和折射效果。

        立方体纹理采样原理:从世界坐标系的坐标原点出发,发射一条射线,与边长为 1 的立方体相交(其中心在坐标原点,并且每个面与对应坐标轴垂直),交点位置的像素即为采样的像素。立方体纹理采样函数如下,cubemap 为立方体纹理,worldVec 为世界坐标系中采样方向向量,color 为采样的颜色。

// 立方体纹理采样
fixed4 color = texCUBE(cubemap, worldVec)

2 反射

        Reflect.shader

Shader "MyShader/Reflection" { // 反射
	Properties {
		_Color("Color Tint", Color) = (1, 1, 1, 1) // 物体颜色
		_ReflectColor("Reflection Color", Color) = (1, 1, 1, 1) // 反射光的颜色
		_ReflectAmount("Reflect Amount", Range(0, 1)) = 1 // 反射比例(用于漫反射和反射之间插值)
		_Cubemap("Reflection Cubemap", Cube) = "_Skybox" {} // 立方体纹理
	}

	SubShader{
		Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}

		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

			fixed4 _Color; // 物体颜色
			fixed4 _ReflectColor; // 反射光的颜色
			fixed _ReflectAmount; // 反射比例(用于漫反射和反射之间插值)
			samplerCUBE _Cubemap; // 立方体纹理

			struct a2v {
				float4 vertex : POSITION; // 模型空间顶点坐标
				float3 normal : NORMAL; // 模型空间法相向量
			};

			struct v2f {
				float4 pos : SV_POSITION; // 裁剪空间顶点坐标
				float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
				fixed3 worldNormal : TEXCOORD1; // 顶点法线向量
				fixed3 worldViewDir : TEXCOORD2; // 观察向量(顶点指向相机)
				fixed3 worldRefl : TEXCOORD3; // 灯光向量(顶点指向光源)
			};

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
				o.worldNormal = UnityObjectToWorldNormal(v.normal); // 计算世界空间中顶点法线向量(已归一化)
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 计算世界空间中顶点坐标
				o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos); // 计算世界空间中观察向量(顶点指向相机)
				o.worldRefl = reflect(-o.worldViewDir, o.worldNormal); // 计算观察向量的反射向量
				return o;
			}

			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal); // 法线向量
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 灯光向量(顶点指向光源)
				fixed3 worldViewDir = normalize(i.worldViewDir); // 观察向量(顶点指向相机)
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 环境光颜色
				fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir)); // 漫反射光颜色
				fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb; // 反射光颜色
				fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount); // 漫反射光与反射光颜色进行插值
				return fixed4(color, 1.0);
			}

			ENDCG
		}
	}

	FallBack "Reflective/VertexLit"
}

         _ReflectAmount 值为 1 反射效果如下:

        _ReflectAmount 值由 0 至 1 渐变反射效果如下:

        说明:即使没有房间模型,中间的 5 个物体也会反射房间环境,这是因为它们使用的纹理源于 Cubemap 采样,而 Cubemap 一旦生成,就与环境无关。 

3 菲涅耳反射

        FresnelReflect.shader

Shader "MyShader/FresnelReflect" { // 菲涅耳反射
	Properties {
		_Color("Color Tint", Color) = (1, 1, 1, 1) // 物体颜色
		_FresnelScale("Fresnel Scale", Range(0, 1)) = 0.5 // 菲涅耳反射系数缩放值
		_Cubemap("Reflection Cubemap", Cube) = "_Skybox" {} // 立方体纹理
	}

	SubShader{
		Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}

		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

			fixed4 _Color; // 物体颜色
			fixed _FresnelScale; // 菲涅耳反射系数缩放值
			samplerCUBE _Cubemap; // 立方体纹理

			struct a2v {
				float4 vertex : POSITION; // 模型空间顶点坐标
				float3 normal : NORMAL; // 模型空间法相向量
			};

			struct v2f {
				float4 pos : SV_POSITION; // 裁剪空间顶点坐标
				float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
				fixed3 worldNormal : TEXCOORD1; // 顶点法线向量
				fixed3 worldViewDir : TEXCOORD2; // 观察向量(顶点指向相机)
				fixed3 worldRefl : TEXCOORD3; // 灯光向量(顶点指向光源)
			};

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
				o.worldNormal = UnityObjectToWorldNormal(v.normal); // 计算世界空间中顶点法线向量(已归一化)
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 计算世界空间中顶点坐标
				o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos); // 计算世界空间中观察向量(顶点指向相机)
				o.worldRefl = reflect(-o.worldViewDir, o.worldNormal); // 计算观察向量的反射向量
				return o;
			}

			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal); // 法线向量
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 灯光向量(顶点指向光源)
				fixed3 worldViewDir = normalize(i.worldViewDir); // 观察向量(顶点指向相机)
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 环境光颜色
				fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir)); // 漫反射光颜色
				fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb; // 反射光颜色
				fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5); // 菲涅耳反射系数
				fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)); // 漫反射光与反射光颜色进行插值
				return fixed4(color, 1.0);
			}

			ENDCG
		}
	}

	FallBack "Reflective/VertexLit"
}

        菲涅耳反射效果如下: 

4 折射

        Refract.shader

Shader "MyShader/Refraction" { // 折射
	Properties {
		_Color("Color Tint", Color) = (1, 1, 1, 1) // 物体颜色
		_RefractColor("Refraction Color", Color) = (1, 1, 1, 1) // 折射光的颜色
		_RefractAmount("Refraction Amount", Range(0, 1)) = 1 // 折射比例(用于漫反射和折射之间插值)
		_RefractRatio("Refraction Ratio", Range(0.1, 1)) = 0.5 // 折射比(入射介质折射率/折射介质折射率)
		_Cubemap("Refraction Cubemap", Cube) = "_Skybox" {} // 立方体纹理
	}

	SubShader{
		Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}

		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

			fixed4 _Color; // 物体颜色
			fixed4 _RefractColor; // 折射光的颜色
			float _RefractAmount; // 折射比例(用于漫反射和折射之间插值)
			fixed _RefractRatio; // 折射比(入射介质折射率/折射介质折射率)
			samplerCUBE _Cubemap; // 立方体纹理

			struct a2v {
				float4 vertex : POSITION; // 模型空间顶点坐标
				float3 normal : NORMAL; // 模型空间法相向量
			};

			struct v2f {
				float4 pos : SV_POSITION; // 裁剪空间顶点坐标
				float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
				fixed3 worldNormal : TEXCOORD1; // 顶点法线向量
				fixed3 worldViewDir : TEXCOORD2; // 观察向量(顶点指向相机)
				fixed3 worldRefr : TEXCOORD3; // 灯光向量(顶点指向光源)
			};

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
				o.worldNormal = UnityObjectToWorldNormal(v.normal); // 计算世界空间中顶点法线向量(已归一化)
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 计算世界空间中顶点坐标
				o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos); // 计算世界空间中观察向量(顶点指向相机)
				o.worldRefr = refract(-normalize(o.worldViewDir), o.worldNormal, _RefractRatio); // 计算观察向量的折射向量
				return o;
			}

			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal); // 法线向量
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 灯光向量(顶点指向光源)
				fixed3 worldViewDir = normalize(i.worldViewDir); // 观察向量(顶点指向相机)
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 环境光颜色
				fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir)); // 漫反射光颜色
				fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb; // 折射光颜色
				fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount); // 漫反射光与折射光颜色进行插值
				return fixed4(color, 1.0);
			}

			ENDCG
		}
	}

	FallBack "Reflective/VertexLit"
}

        _RefractAmount 值为 1 折射效果如下:

        _RefractAmount 值由 0 至 1 渐变折射效果如下:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入printf

目录 printf的定义 printf的使用 函数说明 说明符(specifier) flags(标志) width(最小宽度) .precision(精度) length(类型长度) 转义序列 printf的…

linux(软硬链接)

目录: 1.软连接 2.硬链接 ----------------------------------------------------------------------------------------------------------------------------- 1.软连接 linux当中有两个概念,一个是软连接,一个是硬链接,在学习…

Golang每日一练(leetDay0074) 词典类设计、单词搜索II

目录 211. 添加与搜索单词 - 数据结构设计 Design-add-and-search-words-data-structure 🌟🌟 212. 单词搜索 II Word Search ii 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Rust每日一练 专栏 Golan…

基于C#和Blazor开发的前后端分离框架

Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行。 开源地址 https://gitee.com/known/Known 开发环境 .NET 7VS2022 概述 基于C#和Blazor实现的快速开发框架,前后端分离…

【深度学习】- 作业4: 脑部MRI(核磁共振)图像分别

课程链接: 清华大学驭风计划 代码仓库:Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的,其分为四门课,包括: 机器学习(张敏教授) , 深度学习(胡晓林教授), 计算…

【数据结构与算法】- 周测四

课程链接: 清华大学驭风计划 代码仓库:Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的,其分为四门课,包括: 机器学习(张敏教授) , 深度学习(胡晓林教授), 计算…

三、尚医通医院管理实现

文章目录 三、医院管理实现1、医院列表1.1 医院列表api接口1.1.1 添加service分页接口与实现1.1.2 添加controller方法 1.2 service-cmn模块提供接口1.2.1添加service接口与实现1.2.2添加controller方法 1.3封装Feign服务调用1.3.1搭建service-client父模块1.3.2 搭建service-c…

微生物实验之分菌(细菌)

文章目录 1. 采集实验样本2. 对实验样本进行处理1. 土壤样本的处理2. 植物内生菌样本的处理 3. 接种4. 分离纯化5. 测16s6. 测全基因组7. 保藏菌株 分离细菌菌株 (分菌) 是微生物学实验中,很重要的一环,对于微生物资源来说尤为重要。分菌主要包含以下几个…

人工智能CNN 卷积神经网络结构(tensorflow代码实现)

MNIST是一个简单的视觉计算数据集,它是像下面这样手写的数字图片: MNIST 通过上期的分享,我们了解了手写数字识别的基本原理以及CNN卷积神经网络的基本原理,本期我们结合MNIST数据集,来用代码来实现CNN。(手写数字识别是TensorFlow人工智能最基础的案例,这个跟学习编程…

删除表单(form)元素中的某一个数据项操作实现

问题 对于表单(form)元素,只支持POST请求。若是需要删除表单(form)元素中的某一个数据项,最为严谨的方式是采用POST请求, 对于表单元素,如何转换为DELETE请求 详细问题 对于表单元…

AC规则-2

基于RAM的远程接口 安全元件的访问规则可以通过远程应用程序管理 (RAM) 更新命令进行管理。 因此,ARA-M 和 ARA-C 各自提供一个远程接口,允许在 ARA 中存储或删除访问规则。 访问控制数据的任何远程管理都应仅通过 [GP 卡规范] 定义的安全通道协议来完成…

『树莓派云台机器人』01. 使用手机控制机器人

目录 1. 检查是否已经开机,连接机器人wifi2. 安装树莓派控制app应用,直连模式连接机器人3. 机器人功能实现总结 欢迎关注 『树莓派云台机器人』 博客,持续更新中 欢迎关注 『树莓派云台机器人』 博客,持续更新中 动手组装等步骤请…

chatgpt赋能Python-python_erode

Python Erode:用Python实现图像腐蚀 图像处理是人工智能领域的重要分支,Python是一种广泛应用于机器学习和深度学习的编程语言,也是图像处理领域的主要开发语言之一。在Python中,我们可以使用许多不同的库和工具来处理图像。其中…

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-9

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-9 虚拟机类加载机制类加载的过程准备解析字段解析 方法解析接口方法解析 虚拟机类加载机制 类加载的过程 准备 准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量&#xff09…

检测字符串中所有的字母是否都为大写(字符中的数字、符号和空格不起作用)isupper()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 检测字符串中所有的字母是否都为大写 (字符中的数字、符号和空格不起作用) isupper() 选择题 以下程序的运行结果是? print("【执行】print(PYTHON.isupper())&qu…

chatgpt赋能Python-python_endif

Python Endif: 程序员必备的关键字 如果你是一位有经验的Python程序员,那么你一定非常熟悉Python中的一个关键字:Endif。本文将深入介绍Endif,帮助程序员更好地理解其作用和用法。 什么是Endif Endif是Python中的一个关键字,它…

【自然语言处理】- 作业5: 智能问答在法律智能领域的应用

课程链接: 清华大学驭风计划 代码仓库:Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的,其分为四门课,包括: 机器学习(张敏教授) , 深度学习(胡晓林教授), 计算…

Java如何连接数据库

Java连接MySQL数据库的方法:首先下载解压得到jar库文件,并在对应的项目中导入该库文件;然后添加JDBC;接着在Mysql数据库中进行建表,和添加数据的操作;最后连接数据库并读取数据即可。 Java 连接 MySQL数据库需要驱动包,解压后得到…

springboot国际化多语言配置

文章目录 概要springboot项目为例1 新建路径/文件2 新建两个配置类 搞一个控制器测试总结 概要 项目中有时候会用到多语言的业务场景; 一般来说都是通过后端实现的,将先有内容替换为适用的环境语言; springboot项目为例 1 新建路径/文件 新建路径static/i18n新建文件: mess…

【数据结构与算法】- 周测二

课程链接: 清华大学驭风计划 代码仓库:Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的,其分为四门课,包括: 机器学习(张敏教授) , 深度学习(胡晓林教授), 计算…