效果图:点击地面产生坑!!!
总体思路:
1.用一张RT 标记人走过的痕迹
2.将痕迹映射到雪地上
3.雪地上痕迹凹陷 混合
4. 凹陷和突起,阴影,法线。。。。。。等(未做)
第一步:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Snow : MonoBehaviour
{
public RenderTexture rt;
// 为了混合 rt,但不能直接用rt,所以先用rt0接收中转
RenderTexture rt0;
// 画笔
public Texture drawImg;
// 清屏显示
public Texture defaultImg;
public Camera mainCam;
// 用于混合rt和 画 的材质
public Material drawMat;
void Start()
{
mainCam = Camera.main.GetComponent<Camera>();
GetComponent<Renderer>().material.mainTexture = rt;
DrawDefault();
rt0 = new RenderTexture(rt.width, rt.height, 32, rt.graphicsFormat);
rt0.Create();
}
private void DrawDefault()
{
RenderTexture.active = rt;
GL.PushMatrix();
GL.LoadPixelMatrix(0, rt.width, rt.height, 0);
Rect rect = new Rect(0, 0, rt.width, rt.height);
Graphics.DrawTexture(rect, defaultImg);
GL.PopMatrix();
RenderTexture.active = null;
}
private void Draw(int x,int y)
{
Graphics.Blit(rt, rt0);
RenderTexture.active = rt;
GL.PushMatrix();
// 屏幕空间的表现
GL.LoadPixelMatrix(0, rt.width, rt.height, 0);
// 绘制到中心点
x -= (int)(drawImg.width * 0.5f);
y -= (int)(drawImg.height * 0.5f);
Rect rect = new Rect(x, y, drawImg.width, drawImg.height);
Vector4 SourceUV = new Vector4();
SourceUV.x = rect.x / rt.width;
SourceUV.y = 1 - rect.y / rt.height;
SourceUV.z = rect.width / rt.width;
SourceUV.w = rect.height / rt.width;
SourceUV.y -= SourceUV.w;
drawMat.SetTexture("_SourceTex", rt0);
drawMat.SetVector("_SourceUV", SourceUV);
// 此处如果不用drawMat混合,产生的痕迹会被覆盖掉
//Graphics.DrawTexture(rect, drawImg);
Graphics.DrawTexture(rect, drawImg,drawMat);
GL.PopMatrix();
RenderTexture.active = null;
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
Debug.LogError("按下");
Ray ray = mainCam.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
//多加一个参数 限制射线检测的 距离
//Physics.Raycast(ray, out hit,0.01f)
if (Physics.Raycast(ray,out hit))
{
int x = (int)(hit.textureCoord.x * rt.width);
// (0,0)点在左上角, 变换到左下角 只变动y
int y = (int)(rt.height -hit.textureCoord.y * rt.height);
Debug.LogError("点击到" + hit.transform.name + " x="+ x+" y="+y);
Draw(x, y);
}
}
}
}
第二步:地面的shader,点击地面后,地面shader的顶点偏移,产生“凹陷”的效果
注意理解tex2Dlod函数的意义
Shader "Unlit/snow"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
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;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// 此函数在 顶点着色器,片元着色器中 都能使用,
v.vertex.xyz += tex2Dlod(_MainTex,float4(o.uv,0,0)).r * v.normal;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
}
第三步:能产生凹凸的效果,但是由于顶点数量有限,所以凹凸效果太突兀(可以用曲面细分来优化)。。。
画笔出的痕迹被自己覆盖,所以混合一下,保证是纯色
Shader "Unlit/draw"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_SourceTex("SourceTex", 2D) = "white" {}
_SourceUV("SourceUV",Vector) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType"="Transparent" }
LOD 100
Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
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;
float2 sourceUV : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _SourceTex;
float4 _SourceTex_ST;
Vector _SourceUV;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.sourceUV = TRANSFORM_TEX(_SourceUV, _SourceTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 sCol = tex2D(_SourceTex, i.sourceUV);
fixed4 endCol = min(col, sCol);
return endCol;
}
ENDCG
}
}
}
以上只是宏观上能实现效果,是这么个意思,(美术功底不好不会作图的请脑补一下==),阉割的不能再阉割了。。。。。。 迫于没有图片资源,就不上传效果动图了。
参考:
雪地脚印1
-------------------------分割线--------------------------------------------------------
升级版:
使用曲面细分效果:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
public class DrawWithMind : MonoBehaviour
{
[SerializeField]
public RenderTexture rt;
private Camera mainCam;
public Material drawMat;
Material snowMat;
[Range(0, 100)]
public float _BrushStrength;
[Range(0, 1000)]
public float _BrushSize;
void Start()
{
mainCam = Camera.main.GetComponent<Camera>();
snowMat = GetComponent<MeshRenderer>().material;
rt = new RenderTexture(1024, 1024, 0, GraphicsFormat.R32G32B32A32_SFloat);
snowMat.SetTexture("_MaskTex", rt);
}
Vector4 vec = new Vector4();
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
Ray ray = mainCam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out var hit))
{
vec.x = hit.textureCoord.x;
vec.y = hit.textureCoord.y;
Debug.LogError("hit name =" + hit.transform.name + " pos = " + hit.textureCoord.x + " , " + hit.textureCoord.y + " u=" + vec.x + " v=" + vec.y);
drawMat.SetVector("_HitUV", vec);
//drawMat.SetFloat("_Strength", _BrushStrength);
//drawMat.SetFloat("_powSize", _BrushSize);
RenderTexture temp = RenderTexture.GetTemporary(rt.width, rt.height, 0, rt.format);
Graphics.Blit(rt, temp);
Graphics.Blit(temp, rt, drawMat);
RenderTexture.ReleaseTemporary(temp);
}
}
}
}
Shader "Unlit/DrawLine"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("LineColor",Color) = (0,1,0,1)
[hideIninspector]_HitUV("HitUV",vector) = (0,0,0,0)
_powSize("pow size",Range(0,1000)) = 500
_Strength("Strength",Range(0,60)) = 10
}
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;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
float _powSize;
float _Strength;
vector _HitUV;
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
{
fixed4 col = tex2D(_MainTex, i.uv);
float draw = pow(saturate(1 - distance(i.uv,_HitUV.xy)),500/_powSize);
fixed4 drawCol = _Color * (draw * _Strength);
//col *= draw;
return drawCol+ col;
}
ENDCG
}
}
}
Shader "Unlit/SnowShader"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_BaceTex ("BaceTex", 2D) = "white" {}
_SnowColor("SnowColor", Color) = (1,1,1,1)
_SnowTex("SnowTex", 2D) = "white" {}
_MaskTex("mask tex",2D) = "white"{}
_aaaa("TesselationNum",Range(0.1,100)) = 0.145299
_Displacement("_Displacement",Range(0.1,100)) = 0.5
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma hull hull
#pragma domain domain
#pragma vertex tessvert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Tessellation.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma only_renderers d3d9 d3d11 glcore gles
#pragma target 4.6
sampler2D _BaceTex,_SnowTex,_MaskTex;
float4 _MaskTex_ST ;
float _aaaa;
float4 _Color, _SnowColor;
float _Displacement;
struct VertexInput
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct VertexOutput
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 normal_dir : TEXCOORD1;
float2 uv_bace : TEXCOORD2;
float2 uv_snow : TEXCOORD3;
};
VertexOutput vert(VertexInput v)
{
VertexOutput o = (VertexInput)0;
o.uv = v.uv;
o.normal_dir = UnityObjectToWorldNormal(v.normal);
float4 _MaskTex_var = tex2Dlod(_MaskTex, float4(o.uv, 0, 0));
float4 _BaceTex_var = tex2Dlod(_BaceTex, float4(o.uv, 0, 0));
v.vertex.xyz -= v.normal * (_BaceTex_var.r - 0.7 + _MaskTex_var.r) * _Displacement;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
#ifdef UNITY_CAN_COMPILE_TESSELLATION
struct TessVertex {
float4 vertex : INTERNALTESSPOS;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 uv : TEXCOORD0;
};
struct OutputPatchConstant {
float edge[3] : SV_TessFactor;
float inside : SV_InsideTessFactor;
float3 vTangent[4] : TANGENT;
float2 vUV[4] : TEXCOORD;
float3 vTanUCorner[4] : TANUCORNER;
float3 vTanVCorner[4] : TANVCORNER;
float3 vCWts :TANWELIGHTS;
};
//将顶点里的处理 传入到曲面细分中
TessVertex tessvert(VertexInput v) {
TessVertex o;
o.vertex = v.vertex;
o.normal = v.normal;
o.tangent = v.tangent;
o.uv = v.uv;
return o;
}
// 曲面细分的 远近控制(近处细分,远处不细分)
float4 Tessellation(TessVertex v, TessVertex v1, TessVertex v2) {
float minDist = 1.0;
float maxDist = 25.0;
return UnityDistanceBasedTess(v.vertex, v1.vertex, v2.vertex, minDist, maxDist, _aaaa);
}
// 曲面细分的强度控制
float Tessellation(TessVertex v)
{
return _aaaa;
}
// InputPatch<TessVertex, 3> 表示输入的 patch 为3个 TessVertex 数据,对应三角形 patch 的3个顶点
OutputPatchConstant hullconst(InputPatch<TessVertex, 3> v) {
OutputPatchConstant o = (OutputPatchConstant)0;
float4 ts = Tessellation(v[0], v[1], v[2]);
// 每个边分成几段
o.edge[0] = ts.x;
o.edge[1] = ts.y;
o.edge[2] = ts.z;
// 意味着在三角形内部加入三个新的顶点,但是这个函数并不会创建任何新的顶点,只是会计算出所有新的顶点的重心坐标。
o.inside = ts.w;
return o;
}
[domain("tri")] // 定义特性 输入进hull shader的图元是三角形
[partitioning("fractional_odd")]// 决定分割方式
[outputtopology("triangle_cw")]//决定图元的朝向
[patchconstantfunc("hullconst")]// 补丁常量缓存函数名
[outputcontrolpoints(3)]//决定三个控制点
TessVertex hull(InputPatch<TessVertex, 3> v, uint id : SV_OutputControlPointID) {
return v[id];
}
[domain("tri")]
VertexOutput domain(OutputPatchConstant tessFactors, const OutputPatch<TessVertex, 3> vi, float3 bary : SV_DomainLocation)
{
VertexInput v = (VertexInput)0;
v.vertex = vi[0].vertex * bary.x + vi[1].vertex * bary.y + vi[2].vertex * bary.z;
v.normal = vi[0].normal * bary.x + vi[1].normal * bary.y + vi[2].normal * bary.z;
v.tangent = vi[0].tangent * bary.x + vi[1].tangent * bary.y + vi[2].tangent * bary.z;
v.uv = vi[0].uv * bary.x + vi[1].uv * bary.y + vi[2].uv * bary.z;
VertexOutput o = vert(v);
return o;
}
#endif
fixed4 frag (VertexOutput i) : SV_Target
{
float4 _MaskTex_var = tex2D(_MaskTex, TRANSFORM_TEX(i.uv,_MaskTex));
float4 BaceTex_var = tex2D(_BaceTex, i.uv) * _Color;
float4 SnowTex_var = tex2D(_SnowTex, i.uv) * _SnowColor;
float4 c = lerp(BaceTex_var, SnowTex_var, _MaskTex_var.r);
float3 finalColor = c.xyz;
return fixed4(finalColor,1);
}
ENDCG
}
}
}