最近研究了一下FFT的海水渲染,发现这东西真的涉及到好多好多的数学知识,我基本就是囫囵吞枣,现在基本也忘得差不多了。我在这里挂几个大佬的文章,想研究的可以去看一下,我这个数学渣渣就不强行解释了。
fft海面模拟(一、二、三)
fft ocean注解

Ocean simulation part one: using the discrete Fourier transform
快速傅立叶变换(FFT)的海面模拟
Shader相册第6期 — 实时水面模拟与渲染
【学习笔记】Unity 基于GPU FFT海洋的实现-理论篇
【学习笔记】Unity 基于GPU FFT海洋的实现-实践篇
我自己写了两个版本,一个是CPU得,另一个是GPU版本。
先看一下CPU版本:

这个波浪真得相当好看了,但是这个CPU版本的计算是和顶点绑定在一起的,模型的顶点数没办法太多,我这是64x64,再上去就要卡得不行了。
接下来的是GPU版本,这里就是照搬了后面两篇文章的做法,可以看看效果:

CSDN不能传超过4.5M的图片,下面是视频:

Unity ComputerShader FFT海水渲染

效果真的很不错。
我放一下CPU版本的代码,因为总感觉没代码怪怪的。
代码用的是第三第四篇文章的,涉及到的数学知识太多了,想实现的话还是需要花点时间和耐心得。

using System;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine;
using Random = System.Random;
using Vector2 = UnityEngine.Vector2;
using Vector3 = UnityEngine.Vector3;public class Ocean
{public struct VertexOcean{public Vector3[] vertices;// vertexpublic Vector3[] normals;// normalpublic Vector2[] uvs;//UVpublic Vector2[] htildes;// htilde0public Vector2[] conjugates;// htilde0mk conjugatepublic Vector3[] originals;// original positionpublic Vector2[] uvs2;}public struct ComplexVectorNormal{// structure used with discrete fourier transformpublic Complex h;// wave heightpublic Vector2 D;// displacementpublic Vector3 n;// normal}float g = 9.81f;                                // gravity constantint N, Nplus1;                          // dimension -- N should be a power of 2float A;                                // phillips spectrum parameter -- affects heights of wavesVector2 w;                              // wind parameterfloat length;                           // length parameterRandom rand = new Random();Complex[] tilde, tildeSlopeX, tildeSlopeZ, tildeDx, tildeDz;FFT fft;                              // fast fourier transformpublic VertexOcean oceanData;                    // vertices for vertex buffer objectpublic List<int> indices;                  // indicies for vertex buffer objectint debug = 0;//M=N,L=Lx=Lzpublic Ocean(int N, float A, Vector2 w, float length){tilde = new Complex[N * N];tildeSlopeX = new Complex[N * N];tildeSlopeZ = new Complex[N * N];tildeDx = new Complex[N * N];tildeDz = new Complex[N * N];fft = new FFT(N);indices = new List<int>();this.N = N;Nplus1 = N + 1;this.A = A;this.w = w;this.length = length;oceanData = new VertexOcean();oceanData.vertices = new Vector3[Nplus1 * Nplus1];oceanData.normals = new Vector3[Nplus1 * Nplus1];oceanData.uvs = new Vector2[Nplus1 * Nplus1];oceanData.htildes = new Vector2[Nplus1 * Nplus1];oceanData.conjugates = new Vector2[Nplus1 * Nplus1];oceanData.originals = new Vector3[Nplus1 * Nplus1];oceanData.uvs2 = new Vector2[Nplus1 * Nplus1];int index;Complex htilde0, htilde0mk_conj;for (int m = 0; m < Nplus1; m++){for (int n = 0; n < Nplus1; n++){index = m * Nplus1 + n;htilde0 = hTilde_0(n, m);htilde0mk_conj = Complex.Conjugate(hTilde_0(-n, -m));oceanData.htildes[index].x = (float)htilde0.Real;oceanData.htildes[index].y = (float)htilde0.Imaginary;oceanData.conjugates[index].x = (float)htilde0mk_conj.Real;oceanData.conjugates[index].y = (float)htilde0mk_conj.Imaginary;oceanData.originals[index].x = oceanData.vertices[index].x = (n - N / 2.0f) * length / N;oceanData.originals[index].y = oceanData.vertices[index].y = 0;oceanData.originals[index].z = oceanData.vertices[index].z = (m - N / 2.0f) * length / N;oceanData.normals[index].x = 0.0f;oceanData.normals[index].y = 1.0f;oceanData.normals[index].z = 0.0f;oceanData.uvs[index].x = (float)n / N;oceanData.uvs[index].y = (float)m / N;}}for (int m = 0; m < N; m++){for (int n = 0; n < N; n++){index = m * Nplus1 + n;indices.Add(index);indices.Add(index + Nplus1);indices.Add(index + Nplus1 + 1);indices.Add(index);indices.Add(index + Nplus1 + 1);indices.Add(index + 1);}}}//高斯随机数public Complex GaussRandomVariable(){float s = 0, u = 0, v = 0;while (s > 1 || s == 0){u = (float)rand.NextDouble() * 2 - 1;v = (float)rand.NextDouble() * 2 - 1;s = u * u + v * v;}s = Mathf.Sqrt(-2 * Mathf.Log(s) / s);return new Complex(u * s, v * s);}//菲利普频谱public float Phillips(int n, int m){Vector2 k = new Vector2(Mathf.PI * (2 * n - N) / length, Mathf.PI * (2 * m - N) / length);float k_Length = k.magnitude;if (k_Length < 0.000001) return 0.0f;float k_Length2 = k_Length * k_Length;float k_Length4 = k_Length2 * k_Length2;float KdotW = Vector2.Dot(k.normalized, w.normalized);float KdotW2 = KdotW * KdotW;float w_Length = w.magnitude;float L = w_Length * w_Length / g;float L2 = L * L;float damping = 0.001f;float l2 = L2 * damping * damping;//这里为什么要再乘个 Mathf.Exp(-k_Length * l2)?float phillips = A * Mathf.Exp(-1.0f / (k_Length2 * L2)) / k_Length4 * KdotW2* Mathf.Exp(-k_Length2 * l2);return phillips;}public Complex hTilde_0(int n, int m){Complex r = GaussRandomVariable();return r * Mathf.Sqrt(Phillips(n, m) / 2.0f);}Complex hTilde(float t, int n, int m){int index = m * Nplus1 + n;Complex htilde0 = new Complex(oceanData.htildes[index].x, oceanData.htildes[index].y);Complex htilde0mk_conj= new Complex(oceanData.conjugates[index].x, oceanData.conjugates[index].y);float omegat = Dispersion(n, m) * t;float cos = Mathf.Cos(omegat);float sin = Mathf.Sin(omegat);Complex c0 = new Complex(cos, sin);Complex c1 = new Complex(cos, -sin);Complex res = htilde0 * c0 + htilde0mk_conj * c1;return res;}public float Dispersion(int n, int m){float w0 = 2.0f * Mathf.PI / 200.0f;float kx = Mathf.PI * (2 * n - N) / length;float kz = Mathf.PI * (2 * m - N) / length;//wo是用来做缩放的?return Mathf.Floor(Mathf.Sqrt(g * Mathf.Sqrt(kx * kx + kz * kz) / w0)) * w0;}public ComplexVectorNormal ComputerHDN(Vector2 x,float t){Complex h = new Complex();Vector2 D = Vector2.zero;Vector3 normal = Vector3.zero;Complex c, htilde;Vector2 k;float kx, kz, k_Length, KdotX;for (int m = 0; m < N; m++) {kz = 2.0f * Mathf.PI * (m - N / 2.0f) / length;for (int n = 0; n < N; n++){kx = 2.0f * Mathf.PI * (n - N / 2.0f) / length;k = new Vector2(kx, kz);k_Length = k.magnitude;KdotX = Vector2.Dot(k, x);c = new Complex(Mathf.Cos(KdotX), Mathf.Sin(KdotX));htilde = hTilde(t, n, m) * c;h += htilde;normal += new Vector3(-kx * (float)htilde.Imaginary, 0.0f, -kz * (float)htilde.Imaginary);if (k_Length < 0.000001) continue;D += new Vector2(kx / k_Length * (float)htilde.Imaginary, kz / k_Length * (float)htilde.Imaginary);}}normal = new Vector3(0f, 1f, 0f) - normal;ComplexVectorNormal complex=new ComplexVectorNormal();complex.h = h;complex.D = D;complex.n = normal.normalized;return complex;}public void EvaluateWaves(float t){float kx, kz, len, lambda = -1.0f;int index, index1;for (int m = 0; m < N; m++){kz = Mathf.PI * (2.0f * m - N) / length;for (int n = 0; n < N; n++){kx = Mathf.PI * (2.0f * n - N) / length;len = Mathf.Sqrt(kx * kx + kz * kz);index= m * N + n;tilde[index] = hTilde(t, n, m);tildeSlopeX[index] = tilde[index] * new Complex(0, kx);tildeSlopeZ[index] = tilde[index] * new Complex(0, kz);if(len<0.000001f){tildeDx[index] = new Complex(0f, 0f);tildeDz[index] = new Complex(0f, 0f);}else{tildeDx[index] = tilde[index] * new Complex(0f, -kx / len);tildeDz[index] = tilde[index] * new Complex(0f, -kz / len);}}}for (int m = 0; m < N; m++){fft.ComputeFFT(tilde, ref tilde, 1, m * N);fft.ComputeFFT(tildeSlopeX, ref tildeSlopeX, 1, m * N);fft.ComputeFFT(tildeSlopeZ, ref tildeSlopeZ, 1, m * N);fft.ComputeFFT(tildeDx, ref tildeDx, 1, m * N);fft.ComputeFFT(tildeDz, ref tildeDz, 1, m * N);}for (int n = 0; n < N; n++){fft.ComputeFFT(tilde, ref tilde, N, n);fft.ComputeFFT(tildeSlopeX, ref tildeSlopeX, N, n);fft.ComputeFFT(tildeSlopeZ, ref tildeSlopeZ, N, n);fft.ComputeFFT(tildeDx, ref tildeDx, N, n);fft.ComputeFFT(tildeDz, ref tildeDz, N, n);}int sign;int[] signs = { 1, -1 };Vector3 normal;Vector3[] offsets = ComputeOffset();debug = 0;for (int m = 0; m < N; m++){for (int n = 0; n < N; n++){index = m * N + n;index1 = m * Nplus1 + n;//如果n+m为奇数,则sign=signs[1]=-1;如果n+m为偶数,则sign=signs[0]=1sign = signs[(n + m) & 1];tilde[index] = tilde[index] * sign;tildeDx[index] = tildeDx[index] * sign;tildeDz[index] = tildeDz[index] * sign;Vector3 offset = new Vector3(lambda * (float)tildeDx[index].Real, (float)tilde[index].Real,lambda * (float)tildeDz[index].Real);oceanData.vertices[index1].y = offset.y;oceanData.vertices[index1].x = oceanData.originals[index1].x + offset.x;oceanData.vertices[index1].z = oceanData.originals[index1].z + offset.z;// normaltildeSlopeX[index] = tildeSlopeX[index] * sign;tildeSlopeZ[index] = tildeSlopeZ[index] * sign;normal = new Vector3(-(float)tildeSlopeX[index].Real, 1f, -(float)tildeSlopeZ[index].Real);oceanData.normals[index1] = normal;Vector2 jaco=new Vector2(ComputeJacobian(n, m, offsets),0);//DebugLog(jaco.x.ToString());oceanData.uvs2[index1] = jaco;int number;if (n == 0 && m == 0){number = index1 + N + Nplus1 * N;oceanData.vertices[number].y = offset.y;oceanData.vertices[number].x = oceanData.originals[number].x + offset.x;oceanData.vertices[number].z = oceanData.originals[number].z + offset.z;oceanData.normals[number] = normal;oceanData.uvs2[number] = jaco;}if (n == 0){number = index1 + N;oceanData.vertices[number].y = offset.y;oceanData.vertices[number].x = oceanData.originals[number].x + offset.x;oceanData.vertices[number].z = oceanData.originals[number].z + offset.z;oceanData.normals[number] = normal;oceanData.uvs2[number] = jaco;}if (m == 0){number = index1 + Nplus1 * N;oceanData.vertices[number].y = offset.y;oceanData.vertices[number].x = oceanData.originals[number].x + offset.x;oceanData.vertices[number].z = oceanData.originals[number].z + offset.z;oceanData.normals[number] = normal;oceanData.uvs2[number] = jaco;}}}}void DebugLog(string a){if (debug > 500) return;debug++;Debug.Log(a);}Vector3[] ComputeOffset(){Vector3[] offsets = new Vector3[N*N];float lambda = -1.0f;int index;int[] signs = { 1, -1 };for (int m = 0; m < N; m++){for (int n = 0; n < N; n++){index = m * N + n;int sign = signs[(n + m) & 1];offsets[index] = new Vector3(lambda * (float)(tildeDx[index] * sign).Real,(float)(tilde[index] * sign).Real,lambda * (float)(tildeDz[index] * sign).Real);}}return offsets;}float ComputeJacobian(int n, int m, Vector3[] offsets){int up = m - 1 < 0 ? (N - 1) * N + n : (m - 1) * N + n;int down = m + 1 > N - 1 ? n : (m + 1) * N + n;int left = n - 1 < 0 ? m * N + (N - 1) : m * N + n - 1;int right = n + 1 > N - 1 ? m * N : m * N + n + 1;Vector3 ddx = offsets[down] - offsets[up];Vector3 ddz = offsets[right] - offsets[left];float jacobian = (1.0f + ddx.x) * (1.0f + ddz.z) - ddx.z * ddz.x;return jacobian;}
}
using System.Numerics;
using UnityEngine;public class FFT
{public struct ComplexList{public Complex[] complices;}int N, which;int log2N;float pi2=2*Mathf.PI;uint[] reversed;ComplexList[] T;ComplexList[] c = new ComplexList[2];public FFT(int N){this.N = N;c[0].complices = new Complex[N];c[1].complices = new Complex[N];log2N = (int)(Mathf.Log(N) / Mathf.Log(2));//Debug.Log(log2N);reversed = new uint[N];for (int i = 0; i < N; i++){reversed[i] = Reverse((uint)i);//Debug.Log(reversed[i]);}int pow2 = 1;T = new ComplexList[log2N];for (int i = 0; i < log2N; i++){T[i].complices = new Complex[pow2];for (int j = 0; j < pow2; j++){T[i].complices[j] = ComputeT(j, pow2 * 2);}pow2 *= 2;}which = 0;}//bitreverse算法public uint Reverse(uint i){uint res = 0;for(int j=0;j<log2N;j++){res = (res << 1) + (i & 1);i >>= 1;}return res;}public Complex ComputeT(int x, int n){//Debug.Log(Mathf.Cos((float)pi2 * x / n));return new Complex(Mathf.Cos(pi2 * x / n), Mathf.Sin(pi2 * x / n));}public void ComputeFFT(Complex[] input, ref Complex[] output,int stride,int offset){for (int i = 0; i < N; i++) {c[which].complices[i] = input[reversed[i] * stride + offset];}int loops = N >> 1;int size = 1 << 1;int sizeOver2 = 1;int w = 0;for (int i = 1; i <= log2N; i++){//0,1which ^= 1;for (int j = 0; j < loops; j++){for (int k = 0; k < sizeOver2; k++){c[which].complices[size * j + k] = c[which ^ 1].complices[size * j + k] +c[which ^ 1].complices[size * j + sizeOver2 + k] * T[w].complices[k];}for (int k = sizeOver2; k < size; k++){c[which].complices[size * j + k] = c[which ^ 1].complices[size * j - sizeOver2 + k] -c[which ^ 1].complices[size * j + k] * T[w].complices[k - sizeOver2];}}loops >>= 1;size <<= 1;sizeOver2 <<= 1;w++;}for (int i = 0; i < N; i++){output[i * stride + offset] = c[which].complices[i];}}
}
using UnityEngine;public class OceanManager : MonoBehaviour
{public int N = 32;public float WaveHeight=1f;                                public Vector2 Wind=new Vector2(1f,1f);public float Length=1f;Ocean _ocean;Mesh _mesh;void Start(){_ocean = new Ocean(N, WaveHeight, Wind, Length);_mesh = GetComponent<MeshFilter>().mesh;}private void FixedUpdate(){//Debug.Log(Time.time);_ocean.EvaluateWaves(Time.time);_mesh.vertices = _ocean.oceanData.vertices;_mesh.triangles = _ocean.indices.ToArray();_mesh.uv = _ocean.oceanData.uvs;_mesh.normals = _ocean.oceanData.normals;_mesh.uv2 = _ocean.oceanData.uvs2;//_mesh.RecalculateNormals();}
}

Shader用的是最后两篇的代码,加了一个深度渐变

Shader "Learn/GPUWater"
{Properties{_OceanColor ("Ocean Color",COLOR) = (1,1,1,1)_SpecualrColor ("Specular Color",COLOR) = (1,1,1,1)[HDR]_BubbleColor ("Bubble Color",COLOR) = (1,1,1,1)_MainTex ("Texture", 2D) = "white" {}_Gloss("Gloss", Range(8,200)) = 1_DepthVisibility ("_Depth Visibility", Float) = 1}SubShader{Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True"}LOD 100Pass{Blend SrcAlpha OneMinusSrcAlpha//Cull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include"UnityCG.cginc"#include"Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float3 worldPos:TEXCOORD1;float4 screenPos:TEXCOORD2;};sampler2D _MainTex;sampler2D _Displace;sampler2D _Normal;float4 _MainTex_ST;fixed4 _BubbleColor;fixed4 _OceanColor;fixed4 _SpecualrColor;half _Gloss;half _DepthVisibility;sampler2D _CameraDepthTexture;v2f vert(appdata v){v2f o;o.uv = TRANSFORM_TEX(v.uv, _MainTex);float4 dispalce=tex2Dlod(_Displace,float4(o.uv,0,0));v.vertex+=float4(dispalce.xyz,0);o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;o.vertex = UnityWorldToClipPos(o.worldPos);o.screenPos=ComputeScreenPos(o.vertex);COMPUTE_EYEDEPTH(o.screenPos.z);return o;}fixed4 frag(v2f i) : SV_Target{float3 lightDir=normalize(UnityWorldSpaceLightDir(i.worldPos));float3 viewDir=normalize(UnityWorldSpaceViewDir(i.worldPos));float3 halfDir=normalize(lightDir+viewDir);float4 normalAndBubbles=tex2D(_Normal,i.uv);float3 normal=normalize(UnityObjectToWorldNormal(normalAndBubbles.xyz).rgb);float depth=SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));float sceneZ=max(0,LinearEyeDepth(depth)- _ProjectionParams.g);float partZ=max(0,i.screenPos.z- _ProjectionParams.g);float depthGap = sceneZ - partZ;float x=clamp(depthGap/_DepthVisibility,0,1);half NdotL=saturate(dot(lightDir,normal));fixed3 diffuse = tex2D(_MainTex, i.uv).rgb*NdotL*_OceanColor.rgb;fixed3 bubbles=_BubbleColor.rgb*saturate(dot(lightDir,normal));diffuse=lerp(diffuse,bubbles,normalAndBubbles.a);fixed3 specular=_LightColor0.rgb*_SpecualrColor.rgb*pow(max(0,dot(normal,halfDir)),_Gloss);fixed3 col=diffuse+specular;return fixed4(col,_OceanColor.a*x);}ENDCG}}
}

Unity FFT海水渲染效果展示相关推荐

  1. 【Unity Shader】渲染纹理实现镜子效果

    1 基本概念 1.1 什么是渲染到纹理? 全称是Render To Texture,<入门精要>好像又把渲染目标纹理,即Render Target Texture也叫做RTT,但我认为&l ...

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

    版本:unity 5.4.1  语言:C# 在上节博文实现后,我添加了一些场景的元素,海水的效果大概是这个样子的: 接下来的目标是实现海水的折射和反射,书中使用的Unity应该是4.x时代的,Unit ...

  3. 在Unity中复刻《海岛奇兵》海水渲染

    周末花了一天时间在Unity中复刻了一下<海岛奇兵>海岛场景海水的渲染效果,先上目前复刻的效果图: 总体上还原了海水深度.海面波光.海水流动.海岸泡沫的效果. 复刻的思路其实就是拿Adre ...

  4. 着色器编程_unity中的基础纹理,使用Unity Shader实现基础纹理的渲染效果

    学习通过使用Unity Shader实现基础纹理的渲染效果 目录 学习通过使用Unity Shader实现基础纹理的渲染效果 问1:详细描述一下漫反射纹理.高度纹理.法线纹理.渐变纹理和遮罩纹理? 问 ...

  5. Unity 图片 涂抹/擦除/刮刮乐 效果展示 包含 Sprite, Image, Mesh的涂抹效果

    Unity 2D图片 涂抹/擦除/刮刮乐 效果展示 包含 Sprite, Image, Mesh的涂抹效果 参数 progressText: 显示百分比的文本 maskImg: 需要擦除的图片 bru ...

  6. 米哈游贺甲:如何实现次世代卡通渲染效果?

    在5月12日Unite2017开发者大会上,米哈游技术总监兼美术指导贺甲进行了主题为次世代卡通渲染的演讲.一下为详细分享内容: 大家好,首先自我介绍一下,我叫贺甲,在米哈游担任技术总监和美术指导工作, ...

  7. Unity Shader卡通渲染 · 高清渲染管线·HDRP

    Unity Shader卡通渲染 · 高清渲染管线·HDRP 前言 最近在研究HDRP管线中的卡通渲染,就想着能不能把官方的UCTS移植到HDRP管线里面去,说干就干,到昨天晚上上传了github,今 ...

  8. 第16章 Unity中的渲染优化技术

    程序优化的第一条准则: 不要优化.程序优化的第二条准则(仅针对专家! 〉: 不要优化. 一一Michael A. Jackson 在进行程序优化的时候,人们经常会引用英国的计算机科学家Michael ...

  9. Unity 之 制作翻牌效果

    Unity 之 制作翻牌效果 一,卡牌效果展示 二,翻牌动画制作 三,翻牌动画展示 一,卡牌效果展示 工程图如下: PS: 1. 需要两个Shader来作为牌的正,反面的材质     2. 使用这两个 ...

最新文章

  1. 学习换脸:Switching Eds: Face swapping with Python, dlib, and OpenCV
  2. H264编码之GOP含义
  3. juniper防火墙(SSG and SRX)排障抓包命令
  4. 解决Eclipse Debug 的source not found问题
  5. 剑指offer 求1+2+3+...+n
  6. 微服务的好处与弊端_在云原生时代,就一定要用微服务吗?
  7. ABB机器人套接口通信 机器人部分
  8. 光纤测试时怎么选择对应项目的测试标准及测试仪?
  9. 判定重大风险有哪几种_安全风险识别和评估的方法有哪些?
  10. 12月第三周各国域名总量榜:中国729万居全球第二
  11. Linux系统移植学习
  12. 如何利用ArcScene建立3维地层模型
  13. 游戲外挂,其實是一種網絡藝術
  14. MSM8960和MSM8930
  15. 验证码 工具 Kaptcha 配置参数
  16. 普通代码签名证书和EV代码签名证书的区别
  17. 一切归零,一切从这里开始
  18. sklearn之pipeline:pipeline函数/make_pipeline函数的简介及其区别联系、使用技巧、案例应用之详细攻略
  19. 【Python】Pandas、Numpy性能优化秘籍(全)
  20. 有源医疗器械的开发过程和各阶段的注意事项(九)——————设计验证阶段

热门文章

  1. python中文版编程软件-Python编程软件下载
  2. IntelliJ IDEA激活地址License server
  3. Java代码审计基础——RMI原理和反序列化利用链
  4. 三维实景建模及倾斜摄影建模解决方案,附应用案例
  5. 物资借用/归还登记表模板
  6. 项目一 8 购销合同管理,mybatis的逆向生成工具
  7. 5G专网(private 5G network)解析、应用场景、案例介绍
  8. 5G接入网架构及应用场景
  9. sql server2008导入导出accdb文件
  10. 【latex】3 会议期刊论文模板的使用方法(包括矢量图片处理方法)