准备一张雪地的贴图 以及一张正常的类似地面颜色环境贴图
步骤一:
创建一个shader:snowtracks
关键代码如下:
(1) 声明属性,定义Pass语句块
声明两张图片Snow (RGB)和Ground (RGB)分别是雪的材质和地面的材质
SplatMap用于决定我们调用塌陷测试时使用的判断条件
// An highlighted block
Properties{
_Tess("Tessellation", Range(1,32)) = 4
_SnowColor("Snow Color", color) = (1,1,1,1)
_SnowTex("Snow (RGB)", 2D) = "white" {}
_GroundColor("Ground Color", color) = (1,1,1,1)
_GroundTex("Ground (RGB)", 2D) = "white" {}
_Splat("SplatMap", 2D) = "black" {}
_Displacement("Displacement", Range(0, 1.0)) = 0.3
_Glossiness("Smoothness",Range(0,1)) = 0.5
_Metallic("Metallic",Range(0,1)) = 0.0
}
(2)
// An highlighted block
void surf(Input IN, inout SurfaceOutputStandard o) {
half amount = tex2Dlod(_Splat, float4(IN.uv_Splat,0,0)).r;
fixed4 c =lerp(tex2D(_SnowTex, IN.uv_SnowTex) * _SnowColor, tex2D(_GroundTex, IN.uv_GroundTex) * _GroundColor,amount);
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha =c.a;
}
(3)创建一个材质:
步骤2:
创建一个shader:drawtracks shader
关键代码如下:
// An highlighted block
Shader "Unlit/DrawTracks"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Coordinate("Coordinate",Vector) = (0,0,0,0)
_Color("Draw Color", Color) = (1, 0, 0, 0)
_Size("Size",Range(1,500))=1
_Strength("Strength", Range(0,1))=1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Coordinate, _Color;
half _Size, _Strength;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
float draw = pow(saturate(1 - distance(i.uv, _Coordinate.xy)),500/_Size);
fixed4 drawcol = _Color * (draw*_Strength);
return saturate(col+ drawcol);
return col;
}
ENDCG
}
}
}
用一张纹理图作为积雪溶解的过程,因为每个像素存储的数据不一样就像一个不平的地面,数值比较小的就想隐藏掉,数值比较大的就后隐藏。
步骤3:
创建一个脚本 drawwithmuse,功能是随着鼠标的点击,积雪会发生消融效果,并把脚本赋给plane:
代码如下:
// An highlighted block
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class drawwithmuse : MonoBehaviour
{
// Start is called before the first frame update
public Camera _camera;
public Shader _drawShader;
private RenderTexture _splatmap;
private Material _snowMaterial, _drawMaterial;
[Range(1,500)]
public float _brushSize;
[Range(0, 1)]
public float _brushStrength;
private RaycastHit _hit;
void Start()
{
_drawMaterial = new Material(_drawShader);
_drawMaterial.SetVector("_Color",Color.red);
_snowMaterial = GetComponent<MeshRenderer>().material;
_splatmap = new RenderTexture(1024,1024,0,RenderTextureFormat.ARGBFloat);
_snowMaterial.SetTexture("_Splat",_splatmap);
}
// Update is called once per frame
void Update()
{
if(Input.GetKey(KeyCode.Mouse0)|| Input.GetKey(KeyCode.Mouse1))
{
if(Physics.Raycast(_camera.ScreenPointToRay(Input.mousePosition),out _hit))
{
_drawMaterial.SetVector("_Coordinate", new Vector4(_hit.textureCoord.x, _hit.textureCoord.y, 0, 0));
_drawMaterial.SetFloat("Strength", _brushStrength);
_drawMaterial.SetFloat("Size", _brushSize);
RenderTexture temp = RenderTexture.GetTemporary(_splatmap.width, _splatmap.height, 0, RenderTextureFormat.ARGBFloat);
Graphics.Blit(_splatmap, temp);
Graphics.Blit(temp,_splatmap,_drawMaterial);
RenderTexture.ReleaseTemporary(temp);
}
}
}
private void OnGUI()
{
GUI.DrawTexture(new Rect(0,0,256,256),_splatmap,ScaleMode.ScaleToFit,false,1);
}
}
步骤4:
导入汽车模型:
编写脚本 wheeltrack:
// An highlighted block
void Update()
{
for (int i = 0; i < _wheel.Length; i++)
{
if (Physics.Raycast(_wheel[i].position, -Vector3.up, out _groundHit, 1f, _layerMask))
{
_drawMaterial.SetVector("_Coordinate", new Vector4(_groundHit.textureCoord.x, _groundHit.textureCoord.y, 0, 0));
_drawMaterial.SetFloat("Strength", _brushStrength);
_drawMaterial.SetFloat("Size", _brushSize);
RenderTexture temp = RenderTexture.GetTemporary(_splatmap.width, _splatmap.height, 0, RenderTextureFormat.ARGBFloat);
Graphics.Blit(_splatmap, temp);
Graphics.Blit(temp, _splatmap, _drawMaterial);
RenderTexture.ReleaseTemporary(temp);
}
}
}
(本来是想做轮子划过地面留下划痕,但是没有成功,所以就只贴一部分代码)
把plane 层级改为GROUND
步骤5:在车顶制造积雪效果,创建一个shader:snowshader
因为要造成在z方向上的积雪,所以夹角小于73度的区域将全部填充为积雪颜色,在73度~90度之间(也就是积雪的边缘)有一积雪过渡效果,大于90度的将使用纹理原来颜色。
赋给车,这里代码不贴了,没写好
效果:
步骤6:添加一个粒子特效,营造下雪的效果:
步骤7:把 snowfall 赋给snownoise,让雪掉在地上会逐渐覆盖
(1) 创建 snowfall shader ,造出雪落在地面的效果:
// An highlighted block
Shader "Hidden/SnowFall"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
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;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
float rand(float3 co)
{
return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 45.5432)))*43758.5453);
}
sampler2D _MainTex;
float4 _MainTex_ST;
half _FlakeAmount, _FlakeOpacity;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
float rValue = ceil(rand(float3(i.uv.x, i.uv.y, 0)*_Time.x)-(1- _FlakeAmount));
return saturate(col-(rValue*_FlakeOpacity));
}
ENDCG
}
}
}
(2)创建一个随机的数,让雪的塌陷深度和宽度随机
步骤8:创建一个snownoise脚本 赋给palne
在snow noise 脚本中 实现刚刚声明的snowFallshader
代码片
.
// An highlighted block
public class SnowNoise : MonoBehaviour
{
public Shader _snowFallShader;
private Material _snowFallMat;
private MeshRenderer _meshRenderer;
[Range(0.001f, 0.1f)]
public float _flakeAmount;
[Range(0f, 1f)]
public float _flakeOpacity;
// Start is called before the first frame update
void Start()
{
_meshRenderer = GetComponent<MeshRenderer>();
_snowFallMat = new Material(_snowFallShader);
}
// Update is called once per frame
void Update()
{
_snowFallMat.SetFloat("_FlakeAmount", _flakeAmount);
_snowFallMat.SetFloat("_FlakeOpacity", _flakeOpacity);
RenderTexture snow = (RenderTexture)_meshRenderer.material.GetTexture("_Splat");
RenderTexture temp = RenderTexture.GetTemporary(snow.width, snow.height,0,RenderTextureFormat.ARGBFloat);
Graphics.Blit(snow,temp,_snowFallMat);
Graphics.Blit(temp,snow);
_meshRenderer.material.SetTexture("_Splat", snow);
RenderTexture.ReleaseTemporary(temp);
}
}
步骤9:把 snowfall 赋给snownoise,让雪掉在地上会逐渐覆盖
运行结果:
步骤9:把 snowfall 赋给snownoise,让雪掉在地上会逐渐覆盖
实验效果: 当鼠标按下雪面会发生塌陷效果,鼠标松开后,随着雪落,地面又会慢慢恢复。