ue3dUnity 用于 URP 带曲面细分的交互式雪地

Unity 用于 URP 带曲面细分的交互式雪地

分类:

制作交互式雪地着色器的URP版本,需要两个文件,一个用于镶嵌部分的 .hlsl 文件和一个 .shader 文件,复制代码并新建文件。

SnowTessellation.hlsl 文件

//#ifndef TESSELLATION_CGINC_INCLUDED
//#define TESSELLATION_CGINC_INCLUDED
#if defined(SHADER_API_D3D11) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE) || defined(SHADER_API_VULKAN) || defined(SHADER_API_METAL) || defined(SHADER_API_PSSL)
    #define UNITY_CAN_COMPILE_TESSELLATION 1
    #   define UNITY_domain                 domain
    #   define UNITY_partitioning           partitioning
    #   define UNITY_outputtopology         outputtopology
    #   define UNITY_patchconstantfunc      patchconstantfunc
    #   define UNITY_outputcontrolpoints    outputcontrolpoints
#endif

struct Varyings
{       
    float3 worldPos : TEXCOORD1;
    float3 normal : NORMAL;
    float4 vertex : SV_POSITION;
    float2 uv : TEXCOORD0;
    float3 viewDir : TEXCOORD3;
    float fogFactor : TEXCOORD4;
};

float _Tess;
float _MaxTessDistance;
float _Fade;

struct TessellationFactors
{
    float edge[3] : SV_TessFactor;
    float inside : SV_InsideTessFactor;
};

struct Attributes
{
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float2 uv : TEXCOORD0;    
};

struct ControlPoint
{
    float4 vertex : INTERNALTESSPOS;
    float2 uv : TEXCOORD0;
    float3 normal : NORMAL;   
};

[UNITY_domain("tri")]
[UNITY_outputcontrolpoints(3)]
[UNITY_outputtopology("triangle_cw")]
[UNITY_partitioning("fractional_odd")]
[UNITY_patchconstantfunc("patchConstantFunction")]
ControlPoint hull(InputPatch<ControlPoint, 3> patch, uint id : SV_OutputControlPointID)
{
    return patch[id];
}

TessellationFactors UnityCalcTriEdgeTessFactors (float3 triVertexFactors)
{
    TessellationFactors tess;
    tess.edge[0] = 0.5 * (triVertexFactors.y + triVertexFactors.z);
    tess.edge[1] = 0.5 * (triVertexFactors.x + triVertexFactors.z);
    tess.edge[2] = 0.5 * (triVertexFactors.x + triVertexFactors.y);
    tess.inside = (triVertexFactors.x + triVertexFactors.y + triVertexFactors.z) / 3.0f;
    return tess;
}

float CalcDistanceTessFactor(float4 vertex, float minDist, float maxDist, float tess)
{
				float3 worldPosition = mul(unity_ObjectToWorld, vertex).xyz;
				float dist = distance(worldPosition, _WorldSpaceCameraPos);
				float f = clamp(1.0 - (dist - minDist) / (maxDist - minDist), 0.01, 1.0);
				return f * tess;
}

TessellationFactors DistanceBasedTess(float4 v0, float4 v1, float4 v2, float minDist, float maxDist, float tess)
{
				float3 f;
				f.x = CalcDistanceTessFactor(v0, minDist, maxDist, tess);
				f.y = CalcDistanceTessFactor(v1, minDist, maxDist, tess);
				f.z = CalcDistanceTessFactor(v2, minDist, maxDist, tess);

				return UnityCalcTriEdgeTessFactors(f);
}

uniform float3 _Position;
uniform sampler2D _GlobalEffectRT;
uniform float _OrthographicCamSize;

sampler2D  _Noise;
float _NoiseScale, _SnowHeight, _NoiseWeight, _SnowDepth;

TessellationFactors patchConstantFunction(InputPatch<ControlPoint, 3> patch)
{
    float minDist = 2.0;
    float maxDist = _MaxTessDistance;
    TessellationFactors f;
    return DistanceBasedTess(patch[0].vertex, patch[1].vertex, patch[2].vertex, minDist, maxDist, _Tess);
   
}

float4 GetShadowPositionHClip(Attributes input)
{
    float3 positionWS = TransformObjectToWorld(input.vertex.xyz);
    float3 normalWS = TransformObjectToWorldNormal(input.normal);
 
    float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, 0));
 
#if UNITY_REVERSED_Z
    positionCS.z = min(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
#else
    positionCS.z = max(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
#endif
    return positionCS;
}

Varyings vert(Attributes input)
{
    Varyings output;
    
    float3 worldPosition = mul(unity_ObjectToWorld, input.vertex).xyz;
    //create local uv
    float2 uv = worldPosition.xz - _Position.xz;
    uv = uv / (_OrthographicCamSize * 2);
    uv += 0.5;
    
    // Effects RenderTexture Reading
    float4 RTEffect = tex2Dlod(_GlobalEffectRT, float4(uv, 0, 0));
    // smoothstep to prevent bleeding
   	RTEffect *=  smoothstep(0.99, _Fade, uv.x) * smoothstep(0.99, _Fade,1- uv.x);
	RTEffect *=  smoothstep(0.99, _Fade, uv.y) * smoothstep(0.99, _Fade,1- uv.y);
    
    // worldspace noise texture
    float SnowNoise = tex2Dlod(_Noise, float4(worldPosition.xz * _NoiseScale, 0, 0)).r;
    output.viewDir = SafeNormalize(GetCameraPositionWS() - worldPosition);

	// move vertices up where snow is
	input.vertex.xyz += SafeNormalize(input.normal) * saturate(( _SnowHeight) + (SnowNoise * _NoiseWeight)) * saturate(1-(RTEffect.g * _SnowDepth));

    // transform to clip space
    #ifdef SHADERPASS_SHADOWCASTER
        output.vertex = GetShadowPositionHClip(input);
    #else
        output.vertex = TransformObjectToHClip(input.vertex.xyz);
    #endif

    //outputs
    output.worldPos =  mul(unity_ObjectToWorld, input.vertex).xyz;
    output.normal = input.normal;
    output.uv = input.uv;
    output.fogFactor = ComputeFogFactor(output.vertex.z);
    return output;
}

[UNITY_domain("tri")]
Varyings domain(TessellationFactors factors, OutputPatch<ControlPoint, 3> patch, float3 barycentricCoordinates : SV_DomainLocation)
{
    Attributes v;
    
    #define Interpolate(fieldName) v.fieldName = \
				patch[0].fieldName * barycentricCoordinates.x + \
				patch[1].fieldName * barycentricCoordinates.y + \
				patch[2].fieldName * barycentricCoordinates.z;

    Interpolate(vertex)
    Interpolate(uv)
    Interpolate(normal)
    
    return vert(v);
}

InteractiveSnowURP.shader 文件

Shader "Custom/Snow Interactive" {
	Properties{
		[Header(Main)]
		_Noise("Snow Noise", 2D) = "gray" {}
		_NoiseScale("Noise Scale", Range(0,2)) = 0.1
		_NoiseWeight("Noise Weight", Range(0,2)) = 0.1
		[HDR]_ShadowColor("Shadow Color", Color) = (0.5,0.5,0.5,1)

		[Space]
		[Header(Tesselation)]
		_MaxTessDistance("Max Tessellation Distance", Range(10,100)) = 50
		_Tess("Tessellation", Range(1,32)) = 20

		[Space]
		[Header(Snow)]
		[HDR]_Color("Snow Color", Color) = (0.5,0.5,0.5,1)
		[HDR]_PathColorIn("Snow Path Color In", Color) = (0.5,0.5,0.7,1)
		[HDR]_PathColorOut("Snow Path Color Out", Color) = (0.5,0.5,0.7,1)
		_PathBlending("Snow Path Blending", Range(0,3)) = 0.3
		_MainTex("Snow Texture", 2D) = "white" {}
		_SnowHeight("Snow Height", Range(0,2)) = 0.3
		_SnowDepth("Snow Path Depth", Range(-2,2)) = 0.3
		_SnowTextureOpacity("Snow Texture Opacity", Range(0,2)) = 0.3
		_SnowTextureScale("Snow Texture Scale", Range(0,2)) = 0.3

		[Space]
		[Header(Sparkles)]
		_SparkleScale("Sparkle Scale", Range(0,10)) = 10
		_SparkCutoff("Sparkle Cutoff", Range(0,10)) = 0.8
		_SparkleNoise("Sparkle Noise", 2D) = "gray" {}

		[Space]
		[Header(Rim)]
		_RimPower("Rim Power", Range(0,20)) = 20
		[HDR]_RimColor("Rim Color Snow", Color) = (0.5,0.5,0.5,1)
	}
	HLSLINCLUDE

	// Includes
	#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.universal/ShaderLibrary/Shadows.hlsl"
	#include "SnowTessellation.hlsl"
	#pragma vertex TessellationVertexProgram
	#pragma hull hull
	#pragma domain domain
	// Keywords
	
	#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
	#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
	#pragma multi_compile _ _SHADOWS_SOFT
	#pragma multi_compile_fog


	ControlPoint TessellationVertexProgram(Attributes v)
	{
		ControlPoint p;
		p.vertex = v.vertex;
		p.uv = v.uv;
		p.normal = v.normal;
		return p;
	}
	ENDHLSL

	SubShader{
		Tags{ "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline"}

		Pass{
			Tags { "LightMode" = "UniversalForward" }

			HLSLPROGRAM
			// vertex happens in snowtessellation.hlsl
			#pragma fragment frag
			#pragma target 4.0
			

			sampler2D _MainTex, _SparkleNoise;
			float4 _Color, _RimColor;
			float _RimPower;
			float4 _PathColorIn, _PathColorOut;
			float _PathBlending;
			float _SparkleScale, _SparkCutoff;
			float _SnowTextureOpacity, _SnowTextureScale;
			float4 _ShadowColor;
			

			half4 frag(Varyings IN) : SV_Target{

				// Effects RenderTexture Reading
				float3 worldPosition = mul(unity_ObjectToWorld, IN.vertex).xyz;
				float2 uv = IN.worldPos.xz - _Position.xz;
				uv /= (_OrthographicCamSize * 2);
				uv += 0.5;

				// effects texture				
				float4 effect = tex2D(_GlobalEffectRT, uv);

				// mask to prevent bleeding
				effect *=  smoothstep(0.99, 0.9, uv.x) * smoothstep(0.99, 0.9,1- uv.x);
				effect *=  smoothstep(0.99, 0.9, uv.y) * smoothstep(0.99, 0.9,1- uv.y);

				// worldspace Noise texture
				float3 topdownNoise = tex2D(_Noise, IN.worldPos.xz * _NoiseScale).rgb;

				// worldspace Snow texture
				float3 snowtexture = tex2D(_MainTex, IN.worldPos.xz * _SnowTextureScale).rgb;
				
				//lerp between snow color and snow texture
				float3 snowTex = lerp(_Color.rgb,snowtexture * _Color.rgb, _SnowTextureOpacity);
				
				//lerp the colors using the RT effect path 
				float3 path = lerp(_PathColorOut.rgb * effect.g, _PathColorIn.rgb, saturate(effect.g * _PathBlending));
				float3 mainColors = lerp(snowTex,path, saturate(effect.g));

				// lighting and shadow information
				float shadow = 0;
				half4 shadowCoord = TransformWorldToShadowCoord(IN.worldPos);
				
				#if _MAIN_LIGHT_SHADOWS_CASCADE || _MAIN_LIGHT_SHADOWS
					Light mainLight = GetMainLight(shadowCoord);
					shadow = mainLight.shadowAttenuation;
				#else
					Light mainLight = GetMainLight();
				#endif

				// extra point lights support
				float3 extraLights;
				int pixelLightCount = GetAdditionalLightsCount();
				for (int j = 0; j < pixelLightCount; ++j) {
					Light light = GetAdditionalLight(j, IN.worldPos, half4(1, 1, 1, 1));
					float3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
					extraLights += attenuatedLightColor;			
				}

				float4 litMainColors = float4(mainColors,1) ;
				extraLights *= litMainColors.rgb;
				// add in the sparkles
				float sparklesStatic = tex2D(_SparkleNoise, IN.worldPos.xz * _SparkleScale).r;
				float cutoffSparkles = step(_SparkCutoff,sparklesStatic);				
				litMainColors += cutoffSparkles  *saturate(1- (effect.g * 2)) * 4;
				
				
				// add rim light
				half rim = 1.0 - dot((IN.viewDir), IN.normal) * topdownNoise;
				litMainColors += _RimColor * pow(abs(rim), _RimPower);

				// ambient and mainlight colors added
				half4 extraColors;
				extraColors.rgb = litMainColors.rgb * mainLight.color.rgb * (shadow + unity_AmbientSky.rgb);
				extraColors.a = 1;
				
				// colored shadows
				float3 coloredShadows = (shadow + (_ShadowColor.rgb * (1-shadow)));
				litMainColors.rgb = litMainColors.rgb * mainLight.color * (coloredShadows);
				// everything together
				float4 final = litMainColors+ extraColors + float4(extraLights,0);
				// add in fog
				final.rgb = MixFog(final.rgb, IN.fogFactor);
				return final;

			}
			ENDHLSL

		}

		// casting shadows is a little glitchy, I've turned it off, but maybe in future urp versions it works better?
		// Shadow Casting Pass
		// Pass
		// {
			// 	Name "ShadowCaster"
			// 	Tags { "LightMode" = "ShadowCaster" }
			// 	ZWrite On
			// 	ZTest LEqual
			// 	Cull Off
			
			// 	HLSLPROGRAM
			// 	#pragma target 3.0
			
			// 	// Support all the various light  ypes and shadow paths
			// 	#pragma multi_compile_shadowcaster
			
			// 	// Register our functions
			
			// 	#pragma fragment frag
			// 	// A custom keyword to modify logic during the shadow caster pass

			// 	half4 frag(Varyings IN) : SV_Target{
				// 		return 0;
			// 	}
			
			// 	ENDHLSL
		// }
	}
}

设置:

  • 需要一个向下的正交相机,尺寸 15-ish。
  • 将下面的代码添加到它以将位置和渲染纹理提供给着色器
  • 创建一个新的 RenderTexture 并将其拖到 RT 插槽中,将 Player Transform 添加到 Interactor 插槽。
  • 为正交相机创建一个 新图层 以查看粒子所在的位置,确保主摄像头看不到这一层。
  • 添加 SnowTessellation.hlslInteractiveSnowHLSL.shader 文件,并将它们放在同一个文件夹中。
  • 创建新材料并将其分配给想要下雪的平面。
  • 将距离生成粒子附加到新层上的玩家。

将代码添加到它以将位置和渲染纹理提供给着色器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SetInteractiveShaderEffects : MonoBehaviour
{
    [SerializeField]
    RenderTexture rt;
    [SerializeField]
    Transform target;
    // Start is called before the first frame update
    void Awake()
    {
        Shader.SetGlobalTexture("_GlobalEffectRT", rt);
        Shader.SetGlobalFloat("_OrthographicCamSize", GetComponent<Camera>().orthographicSize);
    }

    private void Update()
    {
        transform.position = new Vector3(target.transform.position.x, transform.position.y, target.transform.position.z);
        Shader.SetGlobalVector("_Position", transform.position);
    }


}

Tessellation:

ue3d - Unity 用于 URP 带曲面细分的交互式雪地

距离细分:

ue3d - Unity 用于 URP 带曲面细分的交互式雪地

Tessellation 是基于距离的,只有靠近相机才会有更高的多边形。最大距离可以用_MaxTessDistance控制

雪道:

ue3d - Unity 用于 URP 带曲面细分的交互式雪地

这是通过播放器上方的 RenderTexture正交摄影机 完成的。玩家将附着在远处产生的粒子,确保这些粒子位于只有正交相机才能看到的新层上。

粒子:

ue3d - Unity 用于 URP 带曲面细分的交互式雪地

作为交互网格的子对象,使用 Start Speed 0 设置新的unity粒子系统。Start Lifetime是想下雪多长时间,把它设置在10 到 15 之间。将 Start Size 设置为与使雪变形的网格一样宽,有轻微的随机变化

发射 中,粒子需要产生 10 Over Distance (超远距离),并确保始终有缩进,还将 Rate over Time (随时间变化的速率) 设置为 1 和 2 之间。形状只是设置为微小的 0.1 边缘。

ue3d - Unity 用于 URP 带曲面细分的交互式雪地

Color over Lifetime 设置为以绿色渐变淡化为 alpha 0(确保它是纯绿色以避免与其他粒子效果混合)

最后在渲染器中,将渲染模式设置为水平广告牌。

材质 设置为标准的白色圆圈,使用 Legacy Shaders/Particles/Additive (Soft) 着色器(或等效的软添加模式),这里使用的是简单的软圆。

ue3d - Unity 用于 URP 带曲面细分的交互式雪地

它现在应该看起来与此类似,就像一条水滴状的小径。大小的变化使它看起来更自然

降雪:

ue3d - Unity 用于 URP 带曲面细分的交互式雪地

快速设置一些下雪效果:

  • 常规:起始大小在 0.5 和 0.7 之间,将最大粒子增加到 2000-ish
  • 发射:时间速率 200
  • 形状:盒子,从体积中发射,x-50 y-50 的比例
  • 生命周期内的速度:世界空间,0.1 和 -0.1 之间的轨道 X/z
  • 渲染器:Billboard 标准粒子着色器
  • 可选择开启碰撞

设置示例:

ue3d - Unity 用于 URP 带曲面细分的交互式雪地

相关信息

  • 类型:知识
  • 字数:660
  • 字符:1813
  • 适用软件:Unity
  • 说明:无
  • 编号:61108

热门内容

提示:3D天堂作为服务提供者,尊重网络版权及知识产权,对某些行为的发生不具备充分的监控能力,若无意间侵犯到您的权利,请 联系我们,我们会在收到信息后尽快给予处理。

本站文章版权归本站自创作者所有,未经允许不得转载!