UnityShader实现水渲染

今天分享一下如何使用Shader实现水体渲染和波浪扰动效果

我们一般可以使用Plane去模拟水平面,创建好Plane后,结合自己项目的风格去搞一个水体贴图,可以在网上下载,我这里直接用这张

1 准备工作完毕,直接上代码

​
Shader "Custom/WaterShader"
{
    Properties
    {
        _BaseColor ("Base Color", Color) = (0, 0.5, 1, 1) // 水的基色
        _NormalMap ("Normal Map", 2D) = "bump" {} // 法线贴图
        _WaveSpeed ("Wave Speed", Float) = 1.0 // 波浪速度
        _WaveStrength ("Wave Strength", Float) = 0.1 // 波浪强度
        _ReflectionStrength ("Reflection Strength", Range(0, 1)) = 0.5 // 反射强度
        _Glossiness ("Glossiness", Range(0, 1)) = 0.5 // 光滑度
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            // 属性
            fixed4 _BaseColor;
            sampler2D _NormalMap;
            float _WaveSpeed;
            float _WaveStrength;
            float _ReflectionStrength;
            float _Glossiness;

            // 顶点输入结构
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            // 顶点输出结构
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 worldPos : TEXCOORD1;
                float3 worldNormal : TEXCOORD2;
            };

            // 顶点着色器
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.worldNormal = UnityObjectToWorldNormal(v.vertex);
                return o;
            }

            // 片段着色器
            fixed4 frag (v2f i) : SV_Target
            {
                // 波浪效果
                float2 waveUV = i.uv + _Time.y * _WaveSpeed;
                float3 normal = UnpackNormal(tex2D(_NormalMap, waveUV));
                normal.xy *= _WaveStrength;
                normal = normalize(normal);

                // 反射计算
                float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
                float3 reflectDir = reflect(-viewDir, normal);
                float4 reflection = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflectDir);

                // 混合基础颜色和反射
                fixed4 color = _BaseColor;
                color.rgb = lerp(color.rgb, reflection.rgb, _ReflectionStrength);

                // 添加光泽效果
                float specular = pow(max(0, dot(reflectDir, viewDir)), _Glossiness * 128);
                color.rgb += specular * _ReflectionStrength;

                return color;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

​

2. 创建材质并应用Shader

  1. 在Unity中创建一个新的材质(Material)。

  2. 将Shader Custom/WaterShader 应用到该材质上。

  3. 将材质应用到场景中的水面平面(Plane)。

  4. .将我们的水题贴图作为法线贴图应用到材质的Normal Map属性中。

3. 调整Shader参数

在材质的Inspector面板中,调整以下参数以优化水的外观:

  • Base Color:设置水的基色(例如浅蓝色)。

  • Normal Map:设置法线贴图以模拟水面波纹。

  • Wave Speed:控制波浪的移动速度。

  • Wave Strength:控制波浪的强度。

  • Reflection Strength:控制反射的强度。

  • Glossiness:控制水面的光滑度。

4. 添加反射探针

为了增强反射效果,可以在场景中添加一个反射探针(Reflection Probe):

  1. 在场景中右键点击 -> Light -> Reflection Probe

  2. 调整反射探针的位置和大小,使其覆盖水面区域。

  3. 在反射探针的设置中,选择RealtimeBaked模式,生成反射贴图。

5.接下来我们实现动态波浪效果

思路:动态修改网格顶点

在Plane上新建一个脚本

脚本代码如下:

using UnityEngine;

[RequireComponent(typeof(MeshFilter))]
public class DynamicWave : MonoBehaviour
{
    public float waveSpeed = 1.0f; // 波浪速度
    public float waveFrequency = 1.0f; // 波浪频率
    public float waveAmplitude = 0.1f; // 波浪幅度

    private Mesh mesh;
    private Vector3[] baseVertices;
    private Vector3[] displacedVertices;

    void Start()
    {
        // 获取网格数据
        mesh = GetComponent().mesh;
        baseVertices = mesh.vertices;
        displacedVertices = new Vector3[baseVertices.Length];
    }

    void Update()
    {
        // 更新波浪效果
        for (int i = 0; i < baseVertices.Length; i++)
        {
            Vector3 vertex = baseVertices[i];
            float wave = Mathf.Sin(vertex.x * waveFrequency + Time.time * waveSpeed) * waveAmplitude;
            vertex.y = wave; // 修改顶点Y坐标以模拟波浪
            displacedVertices[i] = vertex;
        }

        // 更新网格顶点
        mesh.vertices = displacedVertices;
        mesh.RecalculateNormals(); // 重新计算法线
    }
}

脚本参数说明

  • waveSpeed:控制波浪的移动速度。

  • waveFrequency:控制波浪的频率(波浪的数量)。

  • waveAmplitude:控制波浪的高度。

结语

OK! 到这里我们就实现了水渲染,当然这只是基础的实现,有待优化。

比如以下效果,篇幅有限 ,这里就不一一说明了。

焦散效果:使用焦散贴图模拟水底的光线折射。

泡沫效果:在水面边缘添加泡沫效果。

动态波浪:使用脚本或Shader代码生成更复杂的波浪效果。

透明度:调整Shader以支持透明水面,模拟浅水效果。

快自己动手了试试吧!

你可能感兴趣的:(unity,shader)