UnityShader——低多边形水

概述

本文使用C#生成了水的Mesh,通过Shader的时间宏控制Mesh的顶点移动,利用Geometry着色器重新计算法线。
水与物体接触的边缘是用深度图与当前深度的差值来实现的。
效果图

C#代码

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"
}

猜你喜欢

转载自blog.csdn.net/qq_37219491/article/details/105517840