shader实现雪地中的脚印

效果图:点击地面产生坑!!!
在这里插入图片描述

总体思路:
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
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值