一、2D全景图采样
这个 Shader 的核心是将输入的 HDR 环境图像通过反射方向进行采样,并且允许对反射方向进行旋转和调整 MIP 级别。
PS:这里可以将2D图改成CubeMap方式去采样;
Shader "Custom/HDRToCubemapRotateAndMip"
{
Properties
{
_MainTex ("HDR Texture", 2D) = "white" {} // 输入HDR单图
_ReflectColor ("Reflection Color", Color) = (1, 1, 1, 1) // 反射颜色
_RotationAngle ("Rotation Angle", Range(0, 360)) = 0 // 旋转角度
_MipLevel ("Mip Level", Range(0, 10)) = 0 // MIP级别
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float3 worldPos : TEXCOORD0;
float3 normal : TEXCOORD1;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _ReflectColor;
float _RotationAngle;
float _MipLevel; // 新增 MIP 控制参数
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.normal = mul((float3x3)unity_ObjectToWorld, v.normal);
return o;
}
// 从方向向量计算Cubemap UV
float2 DirectionToCubemapUV(float3 dir)
{
float u = atan2(dir.z, dir.x) / (2.0 * UNITY_PI) + 0.5;
float v = asin(dir.y) / UNITY_PI + 0.5;
return float2(u, v);
}
float4 frag (v2f i) : SV_Target
{
// 计算视图方向
float3 viewDir = normalize(i.worldPos - _WorldSpaceCameraPos);
float3 reflectDir = reflect(viewDir, normalize(i.normal));
// 将旋转角度从度数转换为弧度
float radians = _RotationAngle * UNITY_PI / 180.0;
// 计算旋转矩阵
float3x3 rotationMatrix = float3x3(
cos(radians), 0, sin(radians),
0, 1, 0,
-sin(radians), 0, cos(radians)
);
// 应用旋转矩阵到反射方向
reflectDir = mul(rotationMatrix, reflectDir);
// 将反射方向转换为UV坐标
float2 uv = DirectionToCubemapUV(reflectDir);
// 使用 LOD 采样纹理,指定 MIP 级别
float4 hdrColor = tex2Dlod(_MainTex, float4(uv, 0, _MipLevel));
// 结合反射颜色
return hdrColor * _ReflectColor;
}
ENDCG
}
}
}
第二种、2D图轴向挤压
基于盒体投影技术来计算物体反射的逻辑。它通过计算射线与盒子在 3D 空间中的相交点,并基于这个相交点从纹理中采样颜色,沿视线方向挤压。
PS:里面有Cube图的采样方式,可以用作替代;
Shader "Unlit/BoxProjection"
{
Properties
{
// _Cube ("Reflection Cubemap", Cube) = "_Skybox" {}
_MainTex("_MainTex",2D) = "white"{}
_EnvBoxStart ("Env Box Start", Vector) = (0, 0, 0)
_EnvBoxSize ("Env Box Size", Vector) = (1, 1, 1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 viewDir:TEXCOORD1;
float3 objectPos:TEXCOORD2;
};
sampler2D _MainTex;
samplerCUBE _Cube;
float4 _Cube_ST;
float4 _EnvBoxStart;
float4 _EnvBoxSize;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
float3 worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
float3 worldViewDir=UnityWorldSpaceViewDir(worldPos);
o.viewDir=-mul(unity_WorldToObject,float4(worldViewDir,0));
o.objectPos=v.vertex.xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 viewDir=i.viewDir;
float3 objectPos=i.objectPos+half3(0.5,0.5,0);
float3 rbmax=(_EnvBoxStart+_EnvBoxSize-objectPos)/viewDir;
float3 rbmin=(_EnvBoxStart-objectPos)/viewDir;
float3 t2=max(rbmin,rbmax);
//远相交点
float fa=min(min(t2.x,t2.y),t2.z);
float3 posNobox=objectPos+viewDir*fa;
fixed4 col = tex2D(_MainTex, posNobox);
return col;
//反射方向
float3 reflectDir=posNobox-(_EnvBoxStart+_EnvBoxSize/2);
//fixed4 col = texCUBE(_Cube,reflectDir);
return float4(posNobox,1);
}
ENDCG
}
}
}
第三种、2图体积渲染
基于光线步进,模拟体积渲染,我这里层数故意设置高了,实际可以不用这么多;
Shader "Unlit/ParallaxMapping"
{
Properties {
_Color("颜色",Color) = (1,1,1,1)
_MainTex("深度图",2D) = "white"{}
[NoScaleOffset]_BaseTex("颜色图",2D)="white"{}
_Height("位移高度",range(0,1)) = 0.15
_HeightAmount("基准高度",range(0,2)) = 1
_MaxStep("最大迭代步长", Range(4, 256)) = 16
}
SubShader
{
Tags
{
"IgnoreProjector"="True"
"Queue"="Opaque"
"RenderType"="Opaque"
}
Pass
{
Name "FORWARD"
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
sampler2D _BaseTex;
half _Height;
half _HeightAmount;
half4 _Color;
int _MaxStep;
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 viewDir : TEXCOORD2;
};
v2f vert (appdata_full v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
TANGENT_SPACE_ROTATION;
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
return o;
}
float4 frag(v2f i) : SV_Target
{
float3 viewRay=normalize(i.viewDir*-1);
viewRay.z=abs(viewRay.z) + 0.2;
viewRay.xy *= _Height;
float3 shadeP = float3(i.uv,0);
float h2 = _HeightAmount;
float3 lioffset = viewRay / viewRay.z;
float2 pixels = lioffset.xy * _MainTex_TexelSize.zw;
float pixel = max(max(abs(pixels.x), abs(pixels.y)), 1);
float linearStep = min(_MaxStep, floor(pixel));
lioffset /= linearStep;
float d = 1.0 - tex2Dlod(_MainTex, float4(shadeP.xy,0,0)).b * h2 ;
float3 prev_d = d;
float3 prev_shadeP = shadeP;
while(d > shadeP.z)
{
prev_shadeP = shadeP;
shadeP += lioffset;
prev_d = d;
d = 1.0 - tex2Dlod(_MainTex, float4(shadeP.xy,0,0)).b * h2;
}
float d1 = d - shadeP.z;
float d2 = prev_d - prev_shadeP.z;
float w = d1 / (d1 - d2);
shadeP = lerp(shadeP, prev_shadeP, w);
half4 c = tex2D(_BaseTex, shadeP.xy) * _Color;
return c;
}
ENDCG
}
}
}
第四种、2D图盒体投影
这段代码的核心功能是实现盒体投影的反射效果,主要通过计算光线与盒子相交的点,并通过反射方向从立方体贴图(Cubemap)中采样颜色,模拟物体表面的环境反射。
Shader "Unlit/BoxProjection"
{
Properties
{
_Cube ("Reflection Cubemap", Cube) = "_Skybox" {}
_EnvBoxStart ("Env Box Start", Vector) = (0, 0, 0)
_EnvBoxSize ("Env Box Size", Vector) = (1, 1, 1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 viewDir:TEXCOORD1;
float3 objectPos:TEXCOORD2;
};
samplerCUBE _Cube;
float4 _Cube_ST;
float4 _EnvBoxStart;
float4 _EnvBoxSize;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
float3 worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
float3 worldViewDir=UnityWorldSpaceViewDir(worldPos);
o.viewDir=-mul(unity_WorldToObject,float4(worldViewDir,0));
o.objectPos=v.vertex.xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float3 viewDir=i.viewDir;
float3 objectPos=i.objectPos+half3(0.5,0.5,0);
float3 rbmax=(_EnvBoxStart+_EnvBoxSize-objectPos)/viewDir;
float3 rbmin=(_EnvBoxStart-objectPos)/viewDir;
float3 t2=max(rbmin,rbmax);
float fa=min(min(t2.x,t2.y),t2.z);
float3 posNobox=objectPos+viewDir*fa;
float3 reflectDir=posNobox-(_EnvBoxStart+_EnvBoxSize/2);
fixed4 col = texCUBE(_Cube,reflectDir);
return col;
}
ENDCG
}
}
}
第五种、2D图蒙版剔除
这个需要分开来做,球状图单独一个mesh,另外一个面片用来做蒙版剔除显示画面;
SKY:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Common/Men_Stencil" {
Properties {
_DepthValue("远近",Range(0,1)) =1
[Enum(Off, 0, On, 1)]_ZWriteMode ("ZWriteMode", float) = 0
[Enum(UnityEngine.Rendering.CompareFunction)]_ZTestMode ("ZTestMode", Float) = 4
_Tint ("Tint Color", Color) = (.5, .5, .5, .5)
[Gamma] _Exposure ("Exposure", Range(0, 8)) = 1.0
_Rotation ("Rotation", Range(0, 360)) = 0
_RotSpeed ("Rotation Speed", Range(0, 360)) = 0
[NoScaleOffset] _Tex ("Cubemap (HDR)", Cube) = "grey" {}
_DitherStrength("Dither Strength", int) = 16
[IntRange] _StencilRef("Stencil Reference Value", Range(0, 255)) = 0
}
SubShader {
Tags { "RenderPipeline"="UniversalPipeline" "RenderType" = "Transparent" "Queue" = "Transparent" "PreviewType"="Skybox" }
Cull Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
ZWrite [_ZWriteMode]
ZTest [_ZTestMode]
Stencil{
Ref[_StencilRef]
Comp Equal
}
Pass {
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct appdata_t {
float4 vertex : POSITION;
};
struct v2f {
float4 vertex : SV_POSITION;
float3 texcoord : TEXCOORD0;
float3 positionWS : TEXCOORD1;
};
TEXTURECUBE(_Tex);
SAMPLER(sampler_Tex);
CBUFFER_START(UnityPerMaterial)
//CBUFFER_START(UnityPerMaterialCB)
half4 _Tex_HDR;
float4 _Tint;
half _Exposure , _DepthValue;
float _Rotation, _RotSpeed; half _DitherStrength;
CBUFFER_END
#ifdef UNITY_COLORSPACE_GAMMA
#define unity_ColorSpaceDouble half4(2.0, 2.0, 2.0, 2.0)
#else // Linear values
#define unity_ColorSpaceDouble half4(4.59479380, 4.59479380, 4.59479380, 2.0)
#endif
float4 RotateAroundYInDegrees (float4 vertex, float degrees)
{
float alpha = degrees * PI / 180.0;
float sina, cosa;
sincos(alpha, sina, cosa);
float2x2 m = float2x2(cosa, -sina, sina, cosa);
return float4(mul(m, vertex.xz), vertex.yw).xzyw;
}
v2f vert (appdata_t v)
{
v2f o;
o.vertex = TransformObjectToHClip(RotateAroundYInDegrees(v.vertex, _Rotation + (_Time * _RotSpeed)));
// o.texcoord = TransformObjectToWorld(v.vertex.xyz);
o.texcoord = v.vertex.xyz;
o.positionWS = TransformObjectToWorld(v.vertex.xyz);
return o;
}
half4 frag (v2f i) : SV_Target
{
//视角修正
half3 V = SafeNormalize(_WorldSpaceCameraPos - i.positionWS);
half4 tex = SAMPLE_TEXTURECUBE(_Tex,sampler_Tex,float3(i.texcoord.xy,i.texcoord.z*_DepthValue));
half3 c = DecodeHDREnvironment (tex, _Tex_HDR);
c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb;
//c = c * _Tint.rgb * 2.2;
c *= _Exposure;
return float4(c,1);
}
ENDHLSL
}
}
FallBack "Packages/com.unity.render-pipelines.universal/FallbackError"
}
Mask:
Shader "Common/Men_StencilWriter"
{
Properties
{
[IntRange] _StencilRef("Stencil Reference Value", Range(0,255)) = 0
}
SubShader
{
Pass
{
Tags { "Queue" = "Geometry+501" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
ColorMask 0 // Don't write to any colour channels
Stencil
{
Ref[_StencilRef]
Comp Always
Pass Replace
}
Name "Unlit"
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
//GPU批处理
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID //GPU批处理死亡三件套
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float4 color : COLOR;
float3 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID //GPU批处理死亡三件套
};
CBUFFER_START(UnityPerMaterial)
CBUFFER_END
Varyings vert(Attributes v)
{
Varyings o = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(v); //GPU批处理死亡三件套
UNITY_TRANSFER_INSTANCE_ID(v,o); //GPU批处理死亡三件套
o.texcoord = v.positionOS.xyz;
// v.positionOS.xyz = float3(v.positionOS.x,lerp(0,v.positionOS.y,_yMask),v.positionOS.z);
o.color = v.color;
o.positionCS = TransformObjectToHClip(v.positionOS.xyz);
return o;
}
half4 frag(Varyings i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i); //GPU批处理死亡三件套
return 0.5;
}
ENDHLSL
}
}
}
第六种、DDC软件摄像机跟踪
参考三来自B站:视差效果简易制作教程_哔哩哔哩_bilibili
其余查找资料或者GPT找思路....