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 - 片段级模型动态变色