30.高级纹理-立方体纹理-折射-折射结合漫反射及阴影
30.1 知识点
实现带漫反射和阴影的折射效果
主要步骤
新建 Shader
创建一个新的 Shader 文件,并复制上一节Lesson29_RefractionBase
的代码作为基础。优化折射率计算
将折射率 A 和折射率 B 合并为一个折射率比值变量,在外部计算后直接传入 Shader,减少内部计算开销,提高性能。结合漫反射与阴影的折射效果
在实现折射效果的基础上,加入漫反射和阴影的支持。该部分的逻辑与之前的反射实现非常相似,因此可以直接复用部分代码,避免重复书写。- 属性定义
添加与漫反射颜色和折射颜色相关的属性代码。 - 渲染路径与编译指令
复制渲染路径、编译指令以及必要的内置文件,调整属性映射。 - 结构体扩展
增加支持光照和阴影计算所需的结构体字段。 - 顶点着色器修改
调整顶点着色器代码,使其支持新增功能,同时处理阴影计算。 - 片元着色器优化
在片元着色器中结合漫反射、折射效果以及阴影计算,完善最终渲染效果。
- 属性定义
新建一个Shader,复制Lesson29_RefractionBase的代码,取名叫Lesson30_RefractionDiffuseShadow
Shader "Unlit/Lesson30_RefractionDiffuseShadow"
{
Properties
{
//立方体纹理贴图
_Cube("Cubemap", Cube) = ""{}
//介质A折射率
_RefractiveIndexA("RefractiveIndexA", Range(1,2)) = 1
//介质B折射率
_RefractiveIndexB("RefractiveIndexB", Range(1,2)) = 1.3
//折射程度
_RefracAmount("RefracAmount", Range(0,1)) = 1
}
SubShader
{
Tags
{
"RenderType"="Opaque" // 设置渲染类型为不透明(Opaque)
"Queue"="Geometry" // 设置渲染队列为Geometry,通常用于普通几何物体
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
samplerCUBE _Cube;
fixed _RefractiveIndexA;
fixed _RefractiveIndexB;
fixed _RefracAmount;
struct v2f
{
//裁剪空间下顶点坐标
float4 pos:SV_POSITION;
//折射向量
float3 worldRefr:TEXCOORD0;
};
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//法线转世界
fixed3 worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//顶点转世界
fixed3 worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//视角方向获取 摄像机 - 顶点位置 计算反射向量时要取反
fixed3 worldViewDir = UnityWorldSpaceViewDir(worldPos);
//计算折射向量
//第三个参数一定是 介质A/介质B的结果 我们可以声明一个变量在外部算好传进来 这里我们用两个变量只是为了讲解知识
v2f.worldRefr = refract(-normalize(worldViewDir), normalize(worldNormal),
_RefractiveIndexA / _RefractiveIndexB);
return v2f;
}
fixed4 frag(v2f v2f):SV_TARGET
{
//立方体纹理采样
fixed4 cubemapColor = texCUBE(_Cube, v2f.worldRefr);
//结合折射程度进行计算返回
return cubemapColor * _RefracAmount;
}
ENDCG
}
}
}
将折射率A和折射率B 合并为一个 折射率比值变量,以后在外部算好了传入,避免内部计算浪费性能。同时声明漫反射和阴影的属性。处理新增属性的映射。同时添加渲染路径、编译指令、内置文件
Shader "Unlit/Lesson30_RefractionDiffuseShadow"
{
Properties
{
//漫反射颜色
_Color("Color", Color) = (1,1,1,1)
//折射颜色
_RefractColor("RefractColor", Color) = (1,1,1,1)
//折射率比值 介质A折射率/介质B折射率 一般空气比其他的小 所以空气作为A时一般小于1
_RefractRatio("RefractRatio", Range(0.1, 1)) = 0.5
//立方体纹理贴图
_Cube("Cubemap", Cube) = ""{}
//折射程度
_RefracAmount("RefracAmount", Range(0,1)) = 1
}
SubShader
{
Tags
{
"RenderType"="Opaque" // 设置渲染类型为不透明(Opaque)
"Queue"="Geometry" // 设置渲染队列为Geometry,通常用于普通几何物体
}
Pass
{
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//Shader变体
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
//灯光内置文件
#include "AutoLight.cginc"
fixed4 _Color;
fixed4 _RefractColor;
samplerCUBE _Cube;
fixed _RefractRatio;
fixed _RefracAmount;
ENDCG
}
}
}
因为要在片元着色器中处理光和阴影,v2f结构体需要加入世界空间法线\世界空间顶点位置和阴影宏
struct v2f
{
//裁剪空间下顶点坐标
float4 pos:SV_POSITION;
//世界空间下法线
fixed3 worldNormal:NORMAL;
//世界空间下的顶点位置
float3 worldPos:TEXCOORD0;
//折射向量
float3 worldRefr:TEXCOORD1;
//阴影相关
SHADOW_COORDS(2)
};
顶点函数把之前临时变量改成赋值到v2f的变量中,同时添加接受阴影处理宏
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//法线转世界
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//顶点转世界
v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//视角方向获取 摄像机 - 顶点位置
fixed3 worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
//计算折射向量
//第三个参数一定是 介质A/介质B的结果 我们可以声明一个变量在外部算好传进来 这里我们用两个变量只是为了讲解知识
v2f.worldRefr = refract(-normalize(worldViewDir), normalize(v2f.worldNormal), _RefractRatio);
//阴影相关处理
TRANSFER_SHADOW(v2f);
return v2f;
}
片元函数得到光的方向并进行兰伯特漫反射颜色计。保留立方体纹理采样(利用texCUBE函数),使用UNITY_LIGHT_ATTENUATION进行衰减计算。最终利用lerp函数计算最终的颜色,颜色在漫反射颜色和折射颜色之间
fixed4 frag(v2f v2f):SV_TARGET
{
//漫反射光照相关计算
//得到光的方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
//漫反射颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));
//立方体纹理采样
fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefr).rgb * _RefractColor.rgb;
//得到光照衰减以及阴影相关的衰减值
UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);
//我们利用lerp 在漫反射颜色和反射颜色之间 进行插值 0和1就是极限状态 0 没有折射效果 1只有折射效果 0~1之间就是两者叠加
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, _RefracAmount) * atten;
//结合折射程度进行计算返回
return fixed4(color, 1.0);
}
添加FallBack,保证当当前 Shader 不支持某些特性或图形硬件无法完全支持时,可以使用备用 的反射Shader
//内置的阴影投射的Pass
FallBack "Reflective/VertexLit"
创建材质赋值,可以看到有漫反射的效果,和上节课的基础实现有明显区别,同时可以投射和接受阴影,调整折射率和折射程度可以看到效果
30.2 知识点代码
Lesson30_高级纹理_立方体纹理_折射_折射结合漫反射及阴影.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson30_高级纹理_立方体纹理_折射_折射结合漫反射及阴影 : MonoBehaviour
{
void Start()
{
#region 知识点 实现带漫反射和阴影的 折射效果
//1.新建一个Shader,复制Lesson29_RefractionBase的代码
//2.将折射率A和折射率B 合并为一个 折射率比值变量
// 以后在外部算好了传入,避免内部计算浪费性能
//3.折射效果结合漫反射和阴影的做法
// 和之前反射几乎一模一样
// 我们可以直接复制之前的代码
// 避免书写重复内容
// 3-1:属性相关代码复制
// 漫反射颜色和折射颜色
// 3-2:渲染路径、编译指令、内置文件复制
// 属性映射复制修改
// 3-3:结构体相关内容复制
// 3-4:顶点着色器相关内容修改
// 3-5:片元着色器相关内容复制修改
#endregion
}
}
Lesson30_RefractionDiffuseShadow.shader
Shader "Unlit/Lesson30_RefractionDiffuseShadow"
{
Properties
{
//漫反射颜色
_Color("Color", Color) = (1,1,1,1)
//折射颜色
_RefractColor("RefractColor", Color) = (1,1,1,1)
//折射率比值 介质A折射率/介质B折射率 一般空气比其他的小 所以空气作为A时一般小于1
_RefractRatio("RefractRatio", Range(0.1, 1)) = 0.5
//立方体纹理贴图
_Cube("Cubemap", Cube) = ""{}
//折射程度
_RefracAmount("RefracAmount", Range(0,1)) = 1
}
SubShader
{
Tags
{
"RenderType"="Opaque" // 设置渲染类型为不透明(Opaque)
"Queue"="Geometry" // 设置渲染队列为Geometry,通常用于普通几何物体
}
Pass
{
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//Shader变体
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
//灯光内置文件
#include "AutoLight.cginc"
fixed4 _Color;
fixed4 _RefractColor;
samplerCUBE _Cube;
fixed _RefractRatio;
fixed _RefracAmount;
struct v2f
{
//裁剪空间下顶点坐标
float4 pos:SV_POSITION;
//世界空间下法线
fixed3 worldNormal:NORMAL;
//世界空间下的顶点位置
float3 worldPos:TEXCOORD0;
//折射向量
float3 worldRefr:TEXCOORD1;
//阴影相关
SHADOW_COORDS(2)
};
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//法线转世界
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//顶点转世界
v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//视角方向获取 摄像机 - 顶点位置
fixed3 worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
//计算折射向量
//第三个参数一定是 介质A/介质B的结果 我们可以声明一个变量在外部算好传进来 这里我们用两个变量只是为了讲解知识
v2f.worldRefr = refract(-normalize(worldViewDir), normalize(v2f.worldNormal), _RefractRatio);
//阴影相关处理
TRANSFER_SHADOW(v2f);
return v2f;
}
fixed4 frag(v2f v2f):SV_TARGET
{
//漫反射光照相关计算
//得到光的方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
//漫反射颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));
//立方体纹理采样
fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefr).rgb * _RefractColor.rgb;
//得到光照衰减以及阴影相关的衰减值
UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);
//我们利用lerp 在漫反射颜色和反射颜色之间 进行插值 0和1就是极限状态 0 没有折射效果 1只有折射效果 0~1之间就是两者叠加
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, _RefracAmount) * atten;
//结合折射程度进行计算返回
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com