shader之——湖水(河流倒影)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shader之——湖水(河流倒影)相关的知识,希望对你有一定的参考价值。

先上图

技术分享

 

1.水shader

Shader "Game_XXX/whater" {
    Properties {
        _WaterTex ("Normal Map (RGB), Foam (A)", 2D) = "white" {}
        _WaterTex2 ("Normal Map (RGB), Foam (B)", 2D) = "white" {}

        _Tiling ("Wave Scale", Range(0.00025, 0.1)) = 0.25
        _WaveSpeed("Wave Speed", Float) = 0.4

        
        _SpecularRatio ("Specular Ratio", Range(10,500)) = 200

        _BottomColor("Bottom Color",Color) = (0,0,0,0)
        _TopColor("Top Color",Color) = (0,0,0,0)
        _Alpha("Alpha",Range(0,1)) = 1

        _ReflectionTex("_ReflectionTex", 2D) = "black" {}
        _ReflectionLight("ReflectionLight",Range(0,1)) = 0.3

        _LightColorSelf ("LightColorSelf",Color) = (1,1,1,1)
        _LightDir ("LightDir",vector) = (0,1,0,0)

        
        
    }
    
    SubShader {  
        Tags {
            "Queue"="Transparent-200"
            "RenderType"="Transparent" 
            "IgnoreProjector" = "True"
            "LightMode" = "ForwardBase"
        }
        LOD 250
        Pass{
            Lighting On
            ZWrite On
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM

            #pragma vertex Vert
            #pragma fragment Frag
            #include "UnityCG.cginc"
            
             float _Tiling;
             float _WaveSpeed;
             float _SpecularRatio;
             sampler2D _WaterTex;
             sampler2D _WaterTex2;
             sampler2D _ReflectionTex;
             float4 _LightColorSelf;
             float4 _LightDir;
             float4 _BottomColor;
             float4 _TopColor;
             float _Alpha;
             float _ReflectionLight;


            struct v2f
            {
             float4 position  : POSITION;
             float3 worldPos  : TEXCOORD0;
             float3 tilingAndOffset:TEXCOORD2;
             float4 screen:TEXCOORD3;
             float4 VertColor :TEXCOORD4;

            };

    





            v2f Vert(appdata_full v)
            {
             v2f o;
             o.worldPos = mul(unity_ObjectToWorld, v.vertex);
             o.position = UnityObjectToClipPos(v.vertex);
             //uv动画
             o.tilingAndOffset.z =frac( _Time.x * _WaveSpeed);
             o.tilingAndOffset.xy = o.worldPos.xz*_Tiling;
             o.screen = ComputeScreenPos(o.position);
             o.VertColor = v.color;
             return o;
            }



            float4 Frag(v2f i):COLOR
            {
              float3 lightColor=_LightColorSelf.rgb*2;
              //世界视向量
              float3 worldView = -normalize(i.worldPos - _WorldSpaceCameraPos);
              float2 tiling = i.tilingAndOffset.xy;
              //法线采样
              float4 N1 = tex2D(_WaterTex, tiling.yx +float2(i.tilingAndOffset.z,0));
              float4 N2 = tex2D(_WaterTex2, tiling.yx -float2(i.tilingAndOffset.z,0));
              //两个法线相加,转世界空间,这里没有unpack,所以法线贴图不需要转normal  法线贴图为0-1 两张加起来为0-2 将其x2-2,转换为-2 --2然后将其normalize,变成-1到1
              //在遇到两张法线的情况下 ,一般将法线相加 再normalize
              float3 worldNormal  = normalize((N1.xyz+N2.xyz)*2-2);
              //以垂直的方向代替灯光 跟法线做点积 得到漫反射强度
              float LdotN = dot(worldNormal, float3(0,1,0));
              fixed2 uv = i.screen.xy/(i.screen.w+0.0001);
              uv.y = 1-uv.y;


              fixed4 refTex = tex2D (_ReflectionTex,uv + worldNormal.xy*0.02 );






              //这个变量一般在Forward渲染路径下使用,存储的是重要的pixel光源方向,没错,的确是使用w来判断这个光源的类型的,一般和_LightColor0配合使用
              //float3 LView=_WorldSpaceLightPos0.xyz;
              float3 LView = _LightDir.xyz;

                 //if(_WorldSpaceLightPos0.w == 0.0){ 
                    // L = normalize(_WorldSpaceLightPos0.xyz); 
                    // } 
                    // else{ 
                    // L = normalize(_WorldSpaceLightPos0.xyz - i.worldPos); 
                    // } 

            
              
              
                //根据世界法线 ,世界视向量+光向量  得出高光 系数
                float dotSpecular = dot(worldNormal,  normalize( worldView+LView));
                //控制高光的范围
                float3 specularReflection = pow(saturate(dotSpecular), _SpecularRatio);
                
                
  
 
              float4 col;
              float fresnel = 0.5*LdotN+0.5;
              //根据法线的强度 来确定两种颜色之间的混合 ????
              col.rgb  = lerp(_BottomColor.xyz, _TopColor.xyz, fresnel);
             
              col.rgb = saturate (LdotN) *col.rgb;
             
            
              //加上高光
              col.rgb += specularReflection;
              col.rgb = lerp (col.rgb,refTex.rgb*_ReflectionLight,0.7);
              //col.rgb +=refTex.rgb*_ReflectionLight;
              //加上灯光颜色
              col.rgb*=lightColor;
              col.rgb *= i.VertColor.rgb;
              //控制透明度
              
              col.a =i.VertColor.a * _Alpha;
              return col;
            }

        ENDCG    
        }  

    }

    FallBack "Diffuse"
}

2.c# 反射

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class ReflectionSelf : MonoBehaviour {

    private Transform refObject;
    private Camera m_camera;
    public LayerMask reflectionMask;
    private RenderTexture m_texture;
    
    void Start () {
        refObject = this.GetComponent<Transform>();
        GameObject refCramera = new GameObject("refCramera");
        m_camera = refCramera.AddComponent<Camera>();
        m_texture = new RenderTexture(Screen.width,Screen.height,24);
        refCameraSet();
    }
    
    /// <summary>
    /// 相机位置及方向
    /// </summary>
        void cameraTrasform()
    {
        //Position  x z 与mainCamera相同  y 到平面的距离与 mainCamera到平面的距离相等
        Vector3 p_ref;
        Vector3 p_main = Camera.main.transform.position;
        Vector3 p_plan = this.transform.position;
        float y = p_main.y - p_plan.y;

        p_ref.x = p_main.x;
        p_ref.y = p_plan.y - y;
        p_ref.z = p_main.z;
        m_camera.transform.position = p_ref;

        //Rotation
        Vector3 R_ref;
        Vector3 R_main = Camera.main.transform.localEulerAngles;

        R_ref.x = -R_main.x;
        R_ref.y = R_main.y;
        R_ref.z = R_main.z;
        m_camera.transform.localEulerAngles = R_ref;
    }

    /// <summary>
    /// 反射相机的设置
    /// </summary>

        void refCameraSet()
    {
        m_camera.backgroundColor = Color.black;
        m_camera.clearFlags = CameraClearFlags.Skybox; 
        m_camera.cullingMask = reflectionMask; //图层
        m_camera.targetTexture = m_texture;
        this.GetComponent<Renderer>().sharedMaterial.SetTexture("_ReflectionTex", m_camera.targetTexture);
    }

  

    


    void Update () {
        //相机位置要放在这里,因为要随着主相机一直运动
        cameraTrasform();


    }
}

3.曾经在官方的demo里看到反射相机的写法 比较复杂 在此也贴出来 用这个脚本的话 需要把水的shader里面屏幕UV的y方向变为正方向

using UnityEngine;
using System.Collections.Generic;
//[ExecuteInEditMode]
public class ReflectionFx : MonoBehaviour
{
    public Transform[] reflectiveObjects;
    public LayerMask reflectionMask;
    public Material[] reflectiveMaterials;
    private Transform reflectiveSurfaceHeight;
    public Shader replacementShader;
    private bool highQuality = false;
    public Color clearColor = Color.black;
    public System.String reflectionSampler = "_ReflectionTex";

    public float clipPlaneOffset = 0.07F;
    private Vector3 oldpos = Vector3.zero;
    //反射相机
    private Camera reflectionCamera;
    private Dictionary<Camera, bool> helperCameras = null;
    private Texture[] initialReflectionTextures;
    public void Start()
    {
        initialReflectionTextures = new Texture2D[reflectiveMaterials.Length];
        for (int i = 0; i < reflectiveMaterials.Length; i++)
        {
            initialReflectionTextures[i] = reflectiveMaterials[i].GetTexture(reflectionSampler);
        }
       
            this.enabled = true;
    }
    public void OnDisable()
    {
        if (initialReflectionTextures == null)
            return;
        // restore initial reflection textures
        for (int i = 0; i < reflectiveMaterials.Length; i++)
        {
            reflectiveMaterials[i].SetTexture(reflectionSampler, initialReflectionTextures[i]);
        }
    }
    //创建新相机
    private Camera CreateReflectionCameraFor(Camera cam)
    {
        //将string类初始化,挂脚本的物体名 + “Reflection” + 输入的cam名字
        System.String reflName = gameObject.name + "Reflection" + cam.name;
        //Debug.Log("AngryBots: created internal reflection camera " + reflName);
        //找到这个名字的物体,并将go 实例化
        GameObject go = GameObject.Find(reflName);
       
        if (!go) //如果这个物体不存在
            go = new GameObject(reflName, typeof(Camera)); //再重新创建一个新的物体,给他赋上名字和类型
        if (!go.GetComponent(typeof(Camera))) //这个物体没有Camera组件
            go.AddComponent(typeof(Camera));//给它加上Camera组件
        Camera reflectCamera = go.GetComponent<Camera>(); //reflectCamera实例化为这个物体(go)的相机组件
        reflectCamera.backgroundColor = clearColor; //相机的背景颜色 把背景色设置为clearColor 就不会遮盖住背后的视图
        
        reflectCamera.clearFlags = CameraClearFlags.Skybox;//清楚标记,SolidColor:屏幕上的任何空的部分将显示当前相机的背景颜色
        SetStandardCameraParameter(reflectCamera, reflectionMask);//设置渲染图层
        
        if (!reflectCamera.targetTexture) //如果反射相机没有targetTexture
            reflectCamera.targetTexture = CreateTextureFor(cam); //用这个方法创建targetTexture赋给相机的targetTexture
        return reflectCamera;
    }
    public void HighQuality()
    {
        highQuality = true;
    }

    //设置反射相机的渲染图层
    private void SetStandardCameraParameter(Camera cam, LayerMask mask)
    {
        cam.backgroundColor = Color.black; //背景色为黑色
        cam.enabled = true; //cam为false状态?
        cam.cullingMask = reflectionMask; //设置渲染图层
    }
    //给相机赋上targetTexture
    private RenderTexture CreateTextureFor(Camera cam)
    {
        RenderTextureFormat rtFormat = RenderTextureFormat.RGB565;
        if (!SystemInfo.SupportsRenderTextureFormat(rtFormat))
            rtFormat = RenderTextureFormat.Default;
        float rtSizeMul = highQuality ? 0.75f : 0.5f;
        RenderTexture rt = new RenderTexture(Mathf.FloorToInt(cam.pixelWidth * rtSizeMul), Mathf.FloorToInt(cam.pixelHeight * rtSizeMul), 24, rtFormat);
        rt.hideFlags = HideFlags.DontSave;

        return rt;
    }
    public void RenderHelpCameras(Camera currentCam)
    {
        if (null == helperCameras)
            helperCameras = new Dictionary<Camera, bool>();
        if (!helperCameras.ContainsKey(currentCam))
        {
            helperCameras.Add(currentCam, false);
        }
        if (helperCameras[currentCam])
        {
            return;
        }
        if (!reflectionCamera)
        {
            reflectionCamera = CreateReflectionCameraFor(currentCam);
            foreach (Material m in reflectiveMaterials)
            {
                m.SetTexture(reflectionSampler, reflectionCamera.targetTexture);
            }
        }
        RenderReflectionFor(currentCam, reflectionCamera);
        helperCameras[currentCam] = true;
    }
    public void LateUpdate()
    {
        // find the closest reflective surface and use that as our
        // reference for reflection height etc.
        //找到最接近的反射面并将其作为我们的
        //参考高度等。
        Transform closest = null;
        float closestDist = Mathf.Infinity;
        Vector3 pos = Camera.main.transform.position;
        foreach (Transform t in reflectiveObjects)
        {
            if (t.GetComponent<Renderer>().isVisible)
            {
                float dist = (pos - t.position).sqrMagnitude;
                if (dist < closestDist)
                {
                    closestDist = dist;
                    closest = t;
                }
            }
        }
        if (!closest)
            return;
        ObjectBeingRendered(closest, Camera.main);
        if (null != helperCameras)
            helperCameras.Clear();
    }
    private void ObjectBeingRendered(Transform tr, Camera currentCam)
    {
        if (null == tr)
            return;
        reflectiveSurfaceHeight = tr;
        RenderHelpCameras(currentCam);
    }
    private void RenderReflectionFor(Camera cam, Camera reflectCamera)
    {
        if (!reflectCamera)
            return;
        SaneCameraSettings(reflectCamera);
        reflectCamera.backgroundColor = clearColor;
        //GL.SetRevertBackfacing(true);
        GL.invertCulling = true;
        Transform reflectiveSurface = reflectiveSurfaceHeight;
        Vector3 eulerA = cam.transform.eulerAngles;
        reflectCamera.transform.eulerAngles = new Vector3(-eulerA.x, eulerA.y, eulerA.z);
        reflectCamera.transform.position = cam.transform.position;
        Vector3 pos = reflectiveSurface.transform.position;
        pos.y = reflectiveSurface.position.y;
        Vector3 normal = reflectiveSurface.transform.up;
        float d = -Vector3.Dot(normal, pos) - clipPlaneOffset;
        Vector4 reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
        Matrix4x4 reflection = Matrix4x4.zero;
        reflection = CalculateReflectionMatrix(reflection, reflectionPlane);
        oldpos = cam.transform.position;
        Vector3 newpos = reflection.MultiplyPoint(oldpos);
        reflectCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
        Vector4 clipPlane = CameraSpacePlane(reflectCamera, pos, normal, 1.0f);
        Matrix4x4 projection = cam.projectionMatrix;
        projection = CalculateObliqueMatrix(projection, clipPlane);
        reflectCamera.projectionMatrix = projection;
        reflectCamera.transform.position = newpos;
        Vector3 euler = cam.transform.eulerAngles;
        reflectCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);

        reflectCamera.RenderWithShader(replacementShader, "Reflection");
        //GL.SetRevertBackfacing(false);
        GL.invertCulling = false;
    }
    private void SaneCameraSettings(Camera helperCam)
    {
        helperCam.depthTextureMode = DepthTextureMode.None;
        helperCam.backgroundColor = Color.black;
        helperCam.clearFlags = CameraClearFlags.Skybox;
        helperCam.renderingPath = RenderingPath.Forward;
    }
    static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projection, Vector4 clipPlane)
    {
        Vector4 q = projection.inverse * new Vector4(
        sgn(clipPlane.x),
        sgn(clipPlane.y),
        1.0F,
        1.0F
        );
        Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q)));
        // third row = clip plane - fourth row
        projection[2] = c.x - projection[3];
        projection[6] = c.y - projection[7];
        projection[10] = c.z - projection[11];
        projection[14] = c.w - projection[15];
        return projection;
    }

    // Helper function for getting the reflection matrix that will be multiplied with camera matrix
    //用摄像机矩阵来得到反射矩阵的辅助函数
    static Matrix4x4 CalculateReflectionMatrix(Matrix4x4 reflectionMat, Vector4 plane)
    {
        reflectionMat.m00 = (1.0F - 2.0F * plane[0] * plane[0]);
        reflectionMat.m01 = (-2.0F * plane[0] * plane[1]);
        reflectionMat.m02 = (-2.0F * plane[0] * plane[2]);
        reflectionMat.m03 = (-2.0F * plane[3] * plane[0]);
        reflectionMat.m10 = (-2.0F * plane[1] * plane[0]);
        reflectionMat.m11 = (1.0F - 2.0F * plane[1] * plane[1]);
        reflectionMat.m12 = (-2.0F * plane[1] * plane[2]);
        reflectionMat.m13 = (-2.0F * plane[3] * plane[1]);
        reflectionMat.m20 = (-2.0F * plane[2] * plane[0]);
        reflectionMat.m21 = (-2.0F * plane[2] * plane[1]);
        reflectionMat.m22 = (1.0F - 2.0F * plane[2] * plane[2]);
        reflectionMat.m23 = (-2.0F * plane[3] * plane[2]);
        reflectionMat.m30 = 0.0F;
        reflectionMat.m31 = 0.0F;
        reflectionMat.m32 = 0.0F;
        reflectionMat.m33 = 1.0F;

        return reflectionMat;
    }
    // Extended sign: returns -1, 0 or 1 based on sign of a
    //扩展符号:返回-1、0或1,基于a的符号
    static float sgn(float a)
    {
        if (a > 0.0F) return 1.0F;
        if (a < 0.0F) return -1.0F;
        return 0.0F;
    }
    // Given position/normal of the plane, calculates plane in camera space.
    //给定平面的正/正态,在相机空间计算平面
    private Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign)
    {
        Vector3 offsetPos = pos + normal * clipPlaneOffset;
        Matrix4x4 m = cam.worldToCameraMatrix;
        Vector3 cpos = m.MultiplyPoint(offsetPos);
        Vector3 cnormal = m.MultiplyVector(normal).normalized * sideSign;
        return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
    }
}

 

以上是关于shader之——湖水(河流倒影)的主要内容,如果未能解决你的问题,请参考以下文章

Unity Shaders学习笔记之通过修改UV坐标实现纹理贴图的滚动

UnityShader之顶点片段着色器Vertex and Fragment ShaderShader资料

OpenGL 之 Compute Shader(通用计算并行加速)

Cg入门19:Fragment shader - 片段级模型动态变色

Cg入门20:Fragment shader - 片段级模型动态变色(实现汽车动态换漆)

CSS 奇思妙想之酷炫倒影