版本:unity 5.4.1  语言:C#

在上节博文实现后,我添加了一些场景的元素,海水的效果大概是这个样子的:

接下来的目标是实现海水的折射和反射,书中使用的Unity应该是4.x时代的,Unity自带基础包是5.x的,然后我再在网上看了一个例子,看了下来基本原理都差不多。

还记得移动端简单阴影的实现吧,对,就是添加一个相机把照到的玩家传给Shader后,显示在地上,原理是一样的。

首先获取到玩家的相机,新建相机到玩家当前相机,经过一个反射矩阵的旋转后,截取海平面以上的渲染,然后再将渲染出来的Texture传递给Shader处理;折射更加简单,不用矩阵旋转,当前位置的海平面以下渲染出Texture,再传递给Shader。

下面是代码,生成Mesh的代码,我就去掉了:

public class Ocean : MonoBehaviour
{// 一片区域网格横纵数量public int width = 32;public int height = 32;int g_height;   // 组成网格横纵的线条数量int g_width;Vector2 sizeInv;// 区域的数量和大小public int tiles = 2;public Vector3 size = new Vector3(150f, 1f, 150f);// 材质public Material material;public Shader oceanShader;public Transform player;public Transform sun;public Vector4 SunDir;// 网格相关Vector3[] vertices; //顶点Vector3[] normals;  //法线Vector4[] tangents; //三角Mesh baseMesh;// LOD,越在靠后List的Mesh,网格越少int maxLOD = 4;List<List<Mesh>> tiles_LOD;// 折射反射相关public bool renderReflection = true; //是否启用反射折射public int renderTexWidth = 128;public int renderTexHeight = 128;RenderTexture reflectionTexture = null;RenderTexture refractionTexture = null;Camera offscreenCam = null;
bool reflectionRefractionEnabled = false;   //是否初始化完成// Use this for initializationvoid Start(){// 折射反射sizeInv = new Vector2(1f / size.x, 1f / size.z);SetupOffscreenRendering();  // 添加离屏相机if (!renderReflection)EnableReflection(false);elseEnableReflection(true);// 计算线条数量g_height = height + 1;g_width = width + 1;// LOD,Mesh所在的List的LOD List编号越小,Mesh的网格越多tiles_LOD = new List<List<Mesh>>();for (int LOD = 0; LOD < maxLOD; LOD++){tiles_LOD.Add(new List<Mesh>());}for (int y = 0; y < tiles; ++y){for (int x = 0; x < tiles; ++x){Debug.Log("创建了一片水");float cy = y - Mathf.Floor(tiles * 0.5f);float cx = x - Mathf.Floor(tiles * 0.5f);// 创建一片水GameObject tile = new GameObject("WaterTile");// 坐标以当前节点为中心tile.transform.parent = transform;tile.transform.localPosition = new Vector3(cx * size.x, 0f, cy * size.z);// 添加Mesh渲染组件tile.AddComponent<MeshFilter>();tile.AddComponent<MeshRenderer>().material = material;tile.layer = LayerMask.NameToLayer("Water");tiles_LOD[0].Add(tile.GetComponent<MeshFilter>().mesh);}}GenerateHeightmap();}// 设置折射反射void SetupOffscreenRendering(){// 创建折射反射图RecalculateRenderTextures();// 创建Camera实现离屏渲染GameObject cam = new GameObject();cam.name = "DeepWaterOffscreenCam";cam.transform.parent = transform;offscreenCam = cam.AddComponent<Camera>();offscreenCam.clearFlags = CameraClearFlags.Color;offscreenCam.depth = -1;offscreenCam.enabled = false;
}// 当设置reflection和refraction被禁用的时候,设置lod为1void EnableReflection(bool isActive){renderReflection = isActive;if (!isActive)  // 关闭反射折射,使用波光粼粼的图片替代{material.SetTexture("_Reflection", null);material.SetTexture("_Refraction", null);oceanShader.maximumLOD = 1;}else    // 启用反射折射{OnDisable();oceanShader.maximumLOD = 2;RecalculateRenderTextures();}}// 重新生成反射折射的缓存图片void RecalculateRenderTextures(){if (renderReflection){reflectionTexture = new RenderTexture(renderTexWidth, renderTexHeight, 0);refractionTexture = new RenderTexture(renderTexWidth, renderTexHeight, 0);reflectionTexture.wrapMode = TextureWrapMode.Clamp;refractionTexture.wrapMode = TextureWrapMode.Clamp;reflectionTexture.isPowerOfTwo = true;refractionTexture.isPowerOfTwo = true;material.SetTexture("_Reflection", reflectionTexture);material.SetTexture("_Refraction", refractionTexture);material.SetVector("_Size", new Vector4(size.x, size.y, size.z, 0f));}}// 删除反射折射使用的缓存图片void OnDisable(){if (reflectionTexture != null){DestroyImmediate(reflectionTexture);}if (refractionTexture != null){DestroyImmediate(refractionTexture);}reflectionTexture = null;refractionTexture = null;}// 折射反射渲染物体void RenderObject(){if (Camera.current == offscreenCam)return;if (reflectionTexture == null || refractionTexture == null)return;if (renderReflection)RenderReflectionAndRefraction();}public LayerMask renderLayers = -1;// 具体的渲染,使用第二个相机拷贝当前相机的设置void RenderReflectionAndRefraction(){// 获取当前角色身上的主相机Camera renderCamera = Camera.main;Matrix4x4 originalWorldToCam = renderCamera.worldToCameraMatrix;    // 获取世界到相机的矩阵,如果改变了相机的矩阵就不会再根据原Transform渲染,除非调用ResetWorldToCameraMatrixint cullingMask = ~(1 << 4) & renderLayers.value;   //剪裁Mask,忽略水本身// 计算反射矩阵float d = -transform.position.y;Matrix4x4 reflection = Matrix4x4.zero;CameraHelper.CalculateReflectionMatrix(ref reflection, new Vector4(0f, 1f, 0f, d)); //这里不明白,总之弄出了反射矩阵// 根据反射矩阵计算离屏相机位置和矩阵offscreenCam.backgroundColor = RenderSettings.fogColor;offscreenCam.transform.position = reflection.MultiplyPoint(renderCamera.transform.position);    //当前相机置换到反射矩阵中offscreenCam.transform.rotation = renderCamera.transform.rotation;offscreenCam.worldToCameraMatrix = originalWorldToCam * reflection;offscreenCam.cullingMask = cullingMask; //设置剪裁maskoffscreenCam.targetTexture = reflectionTexture; //将反射缓存图片添加到离屏相机里,跟之前阴影是一个原理// 因为反射截取到的图片是翻转的,所以需要设置翻转GL.SetRevertBackfacing(true);// 获取剪裁平面,transform.position.y是当前海水的高度,最后两个值的正负表示剪裁的方向Vector4 cameraSpaceClipPlane = CameraHelper.CameraSpacePlane(offscreenCam, new Vector3(0.0f, transform.position.y, 0.0f), Vector3.up, 1.0f);Matrix4x4 projection = renderCamera.projectionMatrix;   //获得渲染相机的投影矩阵Matrix4x4 obliqueProjection = projection;offscreenCam.fieldOfView = renderCamera.fieldOfView;    //设置FOVoffscreenCam.aspect = renderCamera.aspect;  //设置宽高比CameraHelper.CalculateObliqueMatrix(ref obliqueProjection, cameraSpaceClipPlane);// 开始真正的渲染offscreenCam.projectionMatrix = obliqueProjection;if (!renderReflection)offscreenCam.cullingMask = 0;offscreenCam.Render();GL.SetRevertBackfacing(false);// 折射渲染     offscreenCam.cullingMask = cullingMask;offscreenCam.targetTexture = refractionTexture;obliqueProjection = projection;// 将渲染相机的各个参数设置给离屏相机offscreenCam.transform.position = renderCamera.transform.position;offscreenCam.transform.rotation = renderCamera.transform.rotation;offscreenCam.worldToCameraMatrix = originalWorldToCam;// 获取剪裁平面,计算投影矩阵cameraSpaceClipPlane = CameraHelper.CameraSpacePlane(offscreenCam, new Vector3(0.0f, transform.position.y, 0.0f), Vector3.up, -1.0f);CameraHelper.CalculateObliqueMatrix(ref obliqueProjection, cameraSpaceClipPlane);offscreenCam.projectionMatrix = obliqueProjection;offscreenCam.Render();offscreenCam.projectionMatrix = projection;offscreenCam.targetTexture = null;}
// 初始化Mesh信息,请参考上一节void GenerateHeightmap(){}// 这边应该是Update的,但写在Update中会报GUI Window tries to begin rendering while something else has not finished rendering的错误void OnGUI(){// 设置玩家、太阳角度,并更新反射折射,折射反射是根据玩家视角来计算的if (player == null)player = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();if (sun != null){SunDir = sun.transform.forward;material.SetVector("_SunDir", SunDir);}if (renderReflection)RenderObject();}
}

然后是CameraHelper的脚本:

public class CameraHelper
{private static float sgn(float a){if (a > 0.0f)return 1.0f;if (a < 0.0f)return -1.0f;return 0.0f;}public static void CalculateObliqueMatrix(ref 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)));projection[2] = c.x - projection[3];projection[6] = c.y - projection[7];projection[10] = c.z - projection[11];projection[14] = c.w - projection[15];}public static Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign){Vector3 offsetPos = pos + normal * 0.02f;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));}public static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane){reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);reflectionMat.m01 = (-2F * plane[0] * plane[1]);reflectionMat.m02 = (-2F * plane[0] * plane[2]);reflectionMat.m03 = (-2F * plane[0] * plane[3]);reflectionMat.m10 = (-2F * plane[1] * plane[0]);reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);reflectionMat.m12 = (-2F * plane[1] * plane[2]);reflectionMat.m13 = (-2F * plane[1] * plane[3]);reflectionMat.m20 = (-2F * plane[2] * plane[0]);reflectionMat.m21 = (-2F * plane[2] * plane[1]);reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);reflectionMat.m23 = (-2F * plane[2] * plane[3]);reflectionMat.m30 = 0F;reflectionMat.m31 = 0F;reflectionMat.m32 = 0F;reflectionMat.m33 = 1F;}
}

花了三天时间终于整理出来了比较精简的代码,不过渲染部分的矩阵,我还是没有太理解。

这边提一下作者的Shader,因为是老版本创建的Shader,所以如果颜色空间不使用Gamma的话,反射的倒影会用问题。还有作者的代码是有点问题的,如果想直接用他的代码,最好对比一下我这边的代码,把一些错误排掉。

完成之后的效果是这样的:

有折射和反射效果,但总感觉这个颜色很不对劲,没错,这是上一节留下的一个BUG,Mesh中没有添加法线,你可以自己尝试在Mesh初始化的时候添加,或者像我这样马后炮:

normals = new Vector3[baseMesh.vertices.Length];
for (int i = 0; i < baseMesh.vertices.Length; ++i)normals[i] = Vector3.Normalize(new Vector3(0, 1f, 0));for (int k = 0; k < tiles_LOD[0].Count; ++k)
{Mesh meshLOD = tiles_LOD[0][k];meshLOD.vertices = baseMesh.vertices;meshLOD.normals = normals;meshLOD.tangents = baseMesh.tangents;
}

这段代码放到OnGUI中,不过只要运行一次就好,不然电脑会很卡。。

最终效果:

功夫不负有心人啊,花了我很长的时间,不过对比第一张图和最后一张图的效果,我感觉一切都是值得的,也了解了Unity4.x和5.x的一些区别,总的来说大概明白了折射反射的原理。

下一节的文章,我想挑战一下自己深入了解一下反射矩阵,波浪的东西留到后面再做。

Unity 3D 海水的实现2 折射与反射 离屏相机的渲染相关推荐

  1. Unity shader实现水效果(折射,反射,波浪,1.菲尼尔,深度颜色)

    虽然是AV画质,但是大概还是可以看的 整个实现过程,包括水面的UV流动,折射,反射,根据深度进行透明值处理等等 转载于:https://www.cnblogs.com/ubanck/p/9606626 ...

  2. shader 反射 水面_【Unity Shader】模拟水面包含折射与反射与波浪动画

    最近研究了一下Unity官方的BoatAttack案例,他们模拟的海浪效果很厉害,用的是Gerstner波来模拟水面起伏和波峰的白浪还有浮力系统,还做加入了焦散效果(Caustics)和平面反射(Pl ...

  3. Unity 3D 中的专业“术语表”。

    这是unity手册中的内容.具体可以参考此链接:Unity 用户手册 (2019.4 LTS) - Unity 手册 目录 2D 术语 2D 物理术语 AI 术语 Analytics 术语 动画术语 ...

  4. Unity 3d网游画面的3d效果

    玩家选择某款游戏,第一要素是什么?优质的场景画面和良好的视觉效果,绝对是关键.近年来,但凡是画面上乘的网游,多半会被冠以大作的称号,而这也是能够实现优质画面的游戏引擎备受国内厂商推崇的原因. 就如当前 ...

  5. Unity 3D 环境特效||Unity 3D 游戏场景设计实例

    Unity 3D 环境特效 一般情况下,要在游戏场景中添加雾特效和水特效较为困难,因为需要开发人员懂得着色器语言且能够熟练地使用它进行编程. Unity 3D 游戏开发引擎为了能够简单地还原真实世界中 ...

  6. Unity 3D学习视觉脚本无需编码即可创建高级游戏

    在本课程中,您将学习如何在Unity中使用可视化脚本(以前称为Bolt)以及如何在不编写一行代码的情况下创建自己的高级游戏所需的一切.本课程将教你如何掌握可视化脚本,即使你以前没有任何关于unity或 ...

  7. Unity三维游戏开发C#编程大师班 Masterclass In C# Programing Unity 3D Game Development FPS

    本课程采用现代游戏开发(Unity 2021)的最新内容和最新技术 学习任何东西的最好方法是以一种真正有趣的方式去做,这就是这门课程的来源.如果你想了解你看到的这些不可思议的游戏是如何制作的,没有比这 ...

  8. Unity 3D为策略游戏创建地图学习教程

    MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz,2 Ch 语言:英语+中英文字幕(根据原英文字幕机译更准确) |时长:30节课(7h 42m) |大小:5 GB 含项目文 ...

  9. Unity 3D游戏代码编程学习教程 Full Guide To Unity 3D C#: Learn To Code Making 3D Games

    Unity 3D游戏代码编程学习教程 Full Guide To Unity 3D & C#: Learn To Code Making 3D Games Full Guide To Unit ...

最新文章

  1. 这玩意比ThreadLocal叼多了,吓得我赶紧分享出来。
  2. MonoCon:使用辅助学习的单目3D目标检测框架(AAAI 2022)
  3. url如何定位到Servlet项目
  4. 初学__Python——Python的基本输入输出函数
  5. JDK 环境变量配置
  6. 测试所有类型的Class
  7. SRP6针对于网游登录服的应用
  8. 【第008问 Unity中什么是UV?】
  9. linux中/etc/hosts文件的含义
  10. 用html做高考加油网页,大学学长制作励志视频为高三学子加油
  11. PDF裁剪页面及调整页面大小的方法
  12. 计算机视觉作业(一)Image Filtering and Hybrid Images
  13. 实用命令-pv: 管道查看器
  14. 【转】SCI论文写法攻略
  15. 蓝牙耳机南卡和苹果哪个好用?南卡耳机和苹果耳机深度体验报告
  16. IDEA Debug出现:Skipped breakpoint at because it happened inside debugger evaluation
  17. 【Docker镜像文件加载原理生产中重新制作并提交镜像文件案例演示】
  18. 苹果地图错误将驾车者导向沙漠:偏离70公里
  19. 如何安装PyTorch
  20. 断言(assert)方法

热门文章

  1. 荣耀畅玩8C生猛来袭夺C位,红米Note5看了只能默默躲角落
  2. 2020年国考申论热点:治理“老剧翻拍”乱象
  3. filco蓝牙不好用_FilcoMinilaAir蓝牙机械键盘使用感受,做工精细手感优良但并不完美...
  4. python|面向对象(一)
  5. 深度学习之LSTM案例分析(三)
  6. 【JavaEE进阶系列 | 从小白到工程师】基本类型包装类的使用,装箱以及拆箱与parseInt方法
  7. 香港第一金:黄金3月27行情操作分析预判
  8. CSS3实现图片翻转效果
  9. Git操作不规范,战友提刀来相见。
  10. AutoCAD打开文件提示“无法识别的版本,不能读取”