UnityShader-Low Polygon Water

Overview

This article uses C# to generate a mesh of water, controls the movement of the vertices of the mesh through the time macro of the Shader, and recalculates the normals using the Geometry shader.
The edge of the water contact with the object is realized by the difference between the depth map and the current depth.
Effect picture

C# code

LowpolywaterEditor.cs
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(LowpolyWater))]
public class LowpolyWaterEditor : Editor
{
    
    
    private LowpolyWater _target;
    private void Awake()
    {
    
    
        _target = target as LowpolyWater;
    }

    public override void OnInspectorGUI()
    {
    
    
        base.OnInspectorGUI();
        if (GUILayout.Button("Create Water Plane"))
        {
    
    
            _target.CreateWaterPlane();
        }
    }
}

Lowpolywater.cs
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
[ExecuteInEditMode]
public class LowpolyWater : MonoBehaviour
{
    
    
    [SerializeField]
    private float _unitSize=1.0f;
    [SerializeField]
    private Vector2Int _size=new Vector2Int(2,2);
    [SerializeField]
    private Material _waterMaterial;
    [SerializeField]
    private GameObject _obj;

    private MeshRenderer _meshRenderer;
    private MeshFilter _meshFilter;
    private Mesh _mesh;
    private void Awake()
    {
    
    
        _meshFilter = _obj.GetComponent<MeshFilter>();
        //if (_meshFilter == null)
        //    _meshFilter = _obj.AddComponent<MeshFilter>();
        _meshRenderer = _obj.GetComponent<MeshRenderer>();
        //if (_meshRenderer == null)
        //    _meshRenderer = _obj.AddComponent<MeshRenderer>();
        _mesh = new Mesh();
    }


    public void CreateWaterPlane()
    {
    
    
        Vector3 offset = new Vector3(_size.x / 2 * _unitSize, 0, _size.y / 2 * _unitSize);
        //创建mesh
        _mesh.Clear();
        _meshFilter.mesh = _mesh;
        _meshRenderer.sharedMaterial = _waterMaterial;

        //创建顶点和UV
        Vector3[] vertices = new Vector3[_size.x*_size.y];
        Vector2[] uv = new Vector2[_size.x * _size.y];

        //把uv缩放到0 - 1
        Vector2 uvScale = new Vector2(1.0f/(_size.x - 1), 1.0f / (_size.y - 1));

        for(int x = 0; x < _size.x; x++)
        {
    
    
            for(int y = 0; y < _size.y; y++)
            {
    
    
                vertices[x * _size.y + y] = new Vector3(x, 0, y)*_unitSize- offset;
                uv[x * _size.y + y] = Vector2.Scale(new Vector2(x, y), uvScale);
            }
        }


        _mesh.vertices = vertices;
        _mesh.uv = uv;

        //三角形index
        int[] triangles = new int[(_size.x-1)*(_size.y-1)*6];
        int index = 0;
        Debug.Log(triangles.Length + "   " + (_size.x - 1) * (_size.y - 1) * 6);
        for (int x = 0; x < _size.x-1; x++)
        {
    
    
            for (int y = 0; y < _size.y-1; y++)
            {
    
    
                Debug.Log(x+"   "+ y+"  "+index);
                triangles[index++] = (x * _size.y) + y + 1;
                triangles[index++] = ((x + 1) * _size.y) + y;
                triangles[index++] = (x * _size.y) + y;


                triangles[index++] = (x * _size.y) + y + 1;
                triangles[index++] = ((x + 1) * _size.y) + y + 1;
                triangles[index++] = ((x + 1) * _size.y) + y;
            }
        }

        _mesh.triangles = triangles; //三角面
        _mesh.RecalculateNormals();	//计算法线
    }




}

##Shader

Lowpolywater.shader
Shader "Custom/LowPolyWater" {
Properties { 

	_BaseColor ("Base color", COLOR)  = ( .54, .95, .99, 0.5) 
	_SpecColor ("Specular Material Color", Color) = (1,1,1,1) 
    _Shininess ("Shininess", Float) = 10
	_depthOffset("Depth Offset", float) = 1


	//[MaterialToggle] _isInnerAlphaBlendOrColor("Fade inner to color or alpha?", Float) = 0 

		_WavePos("Wave Position",Vector)=(0,0,0,0)
		_WaveHeight("Wave Height",Float) = 1
		_WaveFrequency("Wave Frequency",Float) = 1

}


CGINCLUDE 

	#include "UnityCG.cginc" 
	#include "UnityLightingCommon.cginc" // for _LightColor0

	sampler2D_float _CameraDepthTexture;
  
	uniform float4 _BaseColor;  
    uniform float _Shininess;
	 
	fixed _depthOffset;
	float4 _WavePos;
	float _WaveHeight;
	float _WaveFrequency;
 
	
	struct v2g
	{
		float4 pos : SV_POSITION;
		float3 vertex:TEXCOORD0;
		float4 scrPos:TEXCOORD1;
		float depth : SV_Depth;
	}; 
 
	struct g2f
	{
		float4 pos : SV_POSITION;
		fixed3 worldNormal : NORMAL;
		float3 vertex:TEXCOORD0;
		float4 scrPos:TEXCOORD1;
		//定义fog贴图坐标
		UNITY_FOG_COORDS(2)
			float depth:SV_Depth;
	};

	v2g vert(appdata_full v)
	{
		v2g o;
		//初始化v2g内的数据
		UNITY_INITIALIZE_OUTPUT(v2g, o);
		//世界坐标和相机的距离向量
		//o.viewInterpolator.xyz = worldSpaceVertex - _WorldSpaceCameraPos;
		//o.worldPos = mul(unity_ObjectToWorld, (v.vertex)).xyz;
		//模型坐标转化为世界坐标
		float3 worldSpaceVertex = mul(unity_ObjectToWorld,(v.vertex)).xyz;
		//通过世界坐标计算波动发起点的距离
		float dis = distance(worldSpaceVertex, _WavePos);
		//计算顶点y坐标
		v.vertex.y = _WaveHeight * sin((_Time.y* _WaveFrequency + dis)*6.28f);

		//顶点坐标转换到裁剪空间
		o.pos = UnityObjectToClipPos(v.vertex);
		o.vertex = v.vertex;

		//ComputeScreenPos COMPUTE_EYEDEPTH 必须在vert内调用 
		o.scrPos = ComputeScreenPos(o.pos);
		//计算当前深度(注意因为ZWrite关闭所以该深度无法在深度图读取)
		COMPUTE_EYEDEPTH(o.depth);
		//UnityWorldSpaceViewDir 得出从顶点到摄像机的向量 等价_WorldSpaceCameraPos - worldPos
       // float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); 

		return o;
	}

	 [maxvertexcount(3)]
	 void geom(triangle v2g input[3], inout TriangleStream<g2f> outStream)
	 {
		fixed3 normal=normalize(UnityObjectToWorldNormal(cross((input[1].vertex - input[0].vertex),(input[2].vertex-input[0].vertex))));
		 for (int i = 0; i<3; i++) 
		 {
			 g2f o = (g2f)0;
			 o.pos = input[i].pos;
			 o.vertex = input[i].vertex;
			 o.depth = input[i].depth;
			 o.worldNormal = normal;
			 o.scrPos = input[i].scrPos;
			 //从顶点数据中输出雾效数据
			 UNITY_TRANSFER_FOG(o, o.pos);
			 //-----将一个顶点添加到输出流列表 
			 outStream.Append(o);
		 }
		 //outStream.RestartStrip();
	 }
	 
	 half4 calculateBaseColor(g2f input)
         {
			float3 worldPos= mul(unity_ObjectToWorld, (input.vertex)).xyz;
            float3 viewDirection = normalize(_WorldSpaceCameraPos - worldPos);
            float3 lightDirection;
            float attenuation;
 
            if (0.0 == _WorldSpaceLightPos0.w) // directional light?
            {
               attenuation = 1.0; // no attenuation
               lightDirection = normalize(_WorldSpaceLightPos0.xyz);
            } 
            else // point or spot light
            {
               float3 vertexToLightSource = 
                  _WorldSpaceLightPos0.xyz - worldPos;
               float distance = length(vertexToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(vertexToLightSource);
            }
 
            float3 ambientLighting = 
               UNITY_LIGHTMODEL_AMBIENT.rgb * _BaseColor.rgb;
 
            float3 diffuseReflection = 
               attenuation * _LightColor0.rgb * _BaseColor.rgb
               * max(0.0, dot(input.worldNormal, lightDirection));
 
            float3 specularReflection;
            if (dot(input.worldNormal, lightDirection) < 0.0)
               // light source on the wrong side?
            {
               specularReflection = float3(0.0, 0.0, 0.0); 
                  // no specular reflection
            }
            else  
            {
               specularReflection = attenuation * _LightColor0.rgb  * _SpecColor.rgb 
				   * pow(max(0.0, dot(reflect(-lightDirection, input.worldNormal), viewDirection)), _Shininess);
            }
            return half4(ambientLighting + diffuseReflection  + specularReflection, _BaseColor.a);
         }

	half4 frag( g2f i ) : SV_Target
	{ 
 
        half4 baseColor = calculateBaseColor(i);
       
		//float4 screenPos = ComputeScreenPos(i.pos);
		//求深度
			half depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos));
			//线性化深度
			depth = LinearEyeDepth(depth);
		

			float diffValue =1-saturate(depth - i.depth);

		//应用fog
		UNITY_APPLY_FOG(i.fogCoord, baseColor);



		//return float4(i.deepth -_deepthOffset, i.deepth -_deepthOffset, i.deepth -_deepthOffset, 1);
		//return float4(depth - _deepthOffset, depth - _deepthOffset, depth - _deepthOffset, 1);
		//return float4(diffValue - _deepthOffset, diffValue - _deepthOffset, diffValue - _deepthOffset, 1);
		//return  calculateBaseColor(i);
		return baseColor+ diffValue *_depthOffset;
	}
	
ENDCG

Subshader
{
	Tags {"RenderType"="Transparent" "Queue"="Transparent"}
	
	Lod 500
	ColorMask RGB
	
	//GrabPass { "_RefractionTex" }
	
	Pass {
			Blend SrcAlpha OneMinusSrcAlpha
			ZTest LEqual
			ZWrite Off
			Cull Off
		
			CGPROGRAM
		
			#pragma target 3.0
		
			#pragma vertex vert
			#pragma geometry geom 
			#pragma fragment frag
			#pragma multi_compile_fog
		
			//#pragma multi_compile WATER_EDGEBLEND_ON WATER_EDGEBLEND_OFF 
		
			ENDCG
	}
}


Fallback "Transparent/Diffuse"
}

Guess you like

Origin blog.csdn.net/qq_37219491/article/details/105517840