wpf 3d水波效果(翻譯文)


 

      很多年前80年代中期,我一個公司,硅谷圖形工作站的工作其中少數炫耀的SGI機器高端圖形演示在一個小線框網格模擬波的傳播這是好玩通過改變網格高度然后又讓模擬運行SGI的機器不夠快由此產生的動畫只是如痴如醉

      在WPF再造水面模擬似乎是一個很好的方式多一點了解WPF中三維圖形 最終的結果是在這里)

     第一步是要找到一種算法,通過水模擬波傳播原來是一個非常簡單算法簡單鄰近點平均身高達到預期的效果在這篇文章中描述的基本算法詳細二維水面相同的算法,也描述了水面解釋的影響

    下一步是成立3D視圖其構成要素兩個不同方向燈創造更多的水面以及確定水面漫反射和鏡面材料的性能對比

   以下是有關XAML注意meshMain將包含水面

 

 1 <Viewport3D Name="viewport3D1" Margin="0,8.181,0,0" Grid.Row="1">
 2     <Viewport3D.Camera>
 3         <PerspectiveCamera x:Name="camMain" Position="48 7.8 41" LookDirection="-48 -7.8 -41" FarPlaneDistance="100" UpDirection="0,1,0" NearPlaneDistance="1" FieldOfView="70">
 4 
 5         </PerspectiveCamera>
 6     </Viewport3D.Camera>
 7     <ModelVisual3D x:Name="vis3DLighting">
 8         <ModelVisual3D.Content>
 9             <DirectionalLight x:Name="dirLightMain" Direction="2, -2, 0"/>
10         </ModelVisual3D.Content>
11     </ModelVisual3D>
12     <ModelVisual3D>
13         <ModelVisual3D.Content>
14             <DirectionalLight Direction="0, -2, 2"/>
15         </ModelVisual3D.Content>
16     </ModelVisual3D>
17     <ModelVisual3D>
18         <ModelVisual3D.Content>
19             <GeometryModel3D x:Name="gmodMain">
20                 <GeometryModel3D.Geometry>
21                     <MeshGeometry3D x:Name="meshMain" >
22                     </MeshGeometry3D>
23                 </GeometryModel3D.Geometry>
24                 <GeometryModel3D.Material>
25                     <MaterialGroup>
26                         <DiffuseMaterial x:Name="matDiffuseMain">
27                             <DiffuseMaterial.Brush>
28                                 <SolidColorBrush Color="DarkBlue"/>
29                             </DiffuseMaterial.Brush>
30                         </DiffuseMaterial>
31                         <SpecularMaterial SpecularPower="24">
32                             <SpecularMaterial.Brush>
33                                 <SolidColorBrush Color="LightBlue"/>
34                             </SpecularMaterial.Brush>
35                         </SpecularMaterial>
36                     </MaterialGroup>
37                 </GeometryModel3D.Material>
38             </GeometryModel3D>
39         </ModelVisual3D.Content>
40     </ModelVisual3D>
41 </Viewport3D>
     下一步我們創建了一個WaveGrid類,實現以上所述的基本算法其基本思路是,我們保持兩個單獨的緩沖區數據表示當前狀態一個以前的狀態 WaveGrid這個數據存儲兩個Point3DCollection對象我們運行仿真我們輪流緩沖區我們正在編寫我們MeshGeometry3D.Positions屬性附加到最近緩沖區請注意,我們只有改變垂直高度Y值

WaveGrid建立了三角形網格指數這也將連接我們的MeshGeometry3D Int32Collection

    所有有趣的東西ProcessWater發生這是我們實現文章描述平滑算法因為我想充分動畫網格中一點處理只是內部點四個相鄰點,但沿網格的邊緣,以及正如我們鄰近點高度值我們保持多少鄰居我們發現使我們能夠平均正確軌道

每個點最終值平滑你的鄰居平均身高速度,這是基本上如何遠離平衡最后一次迭代功能我們也應用阻尼系數因為將逐漸失去它們的振幅

這里的WaveGrid的源代碼
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Windows.Media;
  6 using System.Windows.Media.Media3D;
  7 
  8 namespace WaveSim
  9 {
 10     class WaveGrid
 11     {
 12         // Constants
 13         const int MinDimension = 5;    
 14         const double Damping = 0.96;
 15         const double SmoothingFactor = 2.0;     // Gives more weight to smoothing than to velocity
 16 
 17         // Private member data
 18         private Point3DCollection _ptBuffer1;
 19         private Point3DCollection _ptBuffer2;
 20         private Int32Collection _triangleIndices;
 21 
 22         private int _dimension;
 23 
 24         // Pointers to which buffers contain:
 25         //    - Current: Most recent data
 26         //    - Old: Earlier data
 27         // These two pointers will swap, pointing to ptBuffer1/ptBuffer2 as we cycle the buffers
 28         private Point3DCollection _currBuffer;
 29         private Point3DCollection _oldBuffer;
 30 
 31         /// <summary>
 32         /// Construct new grid of a given dimension
 33         /// </summary>
 34         ///
 35 <param name="Dimension"></param>
 36         public WaveGrid(int Dimension)
 37         {
 38             if (Dimension < MinDimension)
 39                 throw new ApplicationException(string.Format("Dimension must be at least {0}", MinDimension.ToString()));
 40 
 41             _ptBuffer1 = new Point3DCollection(Dimension * Dimension);
 42             _ptBuffer2 = new Point3DCollection(Dimension * Dimension);
 43             _triangleIndices = new Int32Collection((Dimension - 1) * (Dimension - 1) * 2);
 44 
 45             _dimension = Dimension;
 46 
 47             InitializePointsAndTriangles();
 48 
 49             _currBuffer = _ptBuffer2;
 50             _oldBuffer = _ptBuffer1;
 51         }
 52 
 53         /// <summary>
 54         /// Access to underlying grid data
 55         /// </summary>
 56         public Point3DCollection Points
 57         {
 58             get { return _currBuffer; }
 59         }
 60 
 61         /// <summary>
 62         /// Access to underlying triangle index collection
 63         /// </summary>
 64         public Int32Collection TriangleIndices
 65         {
 66             get { return _triangleIndices; }
 67         }
 68 
 69         /// <summary>
 70         /// Dimension of grid--same dimension for both X & Y
 71         /// </summary>
 72         public int Dimension
 73         {
 74             get { return _dimension; }
 75         }
 76 
 77         /// <summary>
 78         /// Set center of grid to some peak value (high point).  Leave
 79         /// rest of grid alone.  Note: If dimension is even, we're not
 80         /// exactly at the center of the grid--no biggie.
 81         /// </summary>
 82         ///
 83         <param name="PeakValue"></param>
 84         public void SetCenterPeak(double PeakValue)
 85         {
 86             int nCenter = (int)_dimension / 2;
 87 
 88             // Change data in oldest buffer, then make newest buffer
 89             // become oldest by swapping
 90             Point3D pt = _oldBuffer[(nCenter * _dimension) + nCenter];
 91             pt.Y = (int)PeakValue;
 92             _oldBuffer[(nCenter * _dimension) + nCenter] = pt;
 93 
 94             SwapBuffers();
 95         }
 96 
 97         /// <summary>
 98         /// Leave buffers in place, but change notation of which one is most recent
 99         /// </summary>
100         private void SwapBuffers()
101         {
102             Point3DCollection temp = _currBuffer;
103             _currBuffer = _oldBuffer;
104             _oldBuffer = temp;
105         }
106 
107         /// <summary>
108         /// Clear out points/triangles and regenerates
109         /// </summary>
110         ///
111         <param name="grid"></param>
112         private void InitializePointsAndTriangles()
113         {
114             _ptBuffer1.Clear();
115             _ptBuffer2.Clear();
116             _triangleIndices.Clear();
117 
118             int nCurrIndex = 0;     // March through 1-D arrays
119 
120             for (int row = 0; row < _dimension; row++)
121             {
122                 for (int col = 0; col < _dimension; col++)
123                 {
124                     // In grid, X/Y values are just row/col numbers
125                     _ptBuffer1.Add(new Point3D(col, 0.0, row));
126 
127                     // Completing new square, add 2 triangles
128                     if ((row > 0) && (col > 0))
129                     {
130                         // Triangle 1
131                         _triangleIndices.Add(nCurrIndex - _dimension - 1);
132                         _triangleIndices.Add(nCurrIndex);
133                         _triangleIndices.Add(nCurrIndex - _dimension);
134 
135                         // Triangle 2
136                         _triangleIndices.Add(nCurrIndex - _dimension - 1);
137                         _triangleIndices.Add(nCurrIndex - 1);
138                         _triangleIndices.Add(nCurrIndex);
139                     }
140 
141                     nCurrIndex++;
142                 }
143             }
144 
145             // 2nd buffer exists only to have 2nd set of Z values
146             _ptBuffer2 = _ptBuffer1.Clone();
147         }
148 
149         /// <summary>
150         /// Determine next state of entire grid, based on previous two states.
151         /// This will have the effect of propagating ripples outward.
152         /// </summary>
153         public void ProcessWater()
154         {
155             // Note that we write into old buffer, which will then become our
156             //    "current" buffer, and current will become old. 
157             // I.e. What starts out in _currBuffer shifts into _oldBuffer and we
158             // write new data into _currBuffer.  But because we just swap pointers,
159             // we don't have to actually move data around.
160 
161             // When calculating data, we don't generate data for the cells around
162             // the edge of the grid, because data smoothing looks at all adjacent
163             // cells.  So instead of running [0,n-1], we run [1,n-2].
164 
165             double velocity;    // Rate of change from old to current
166             double smoothed;    // Smoothed by adjacent cells
167             double newHeight;
168             int neighbors;
169 
170             int nPtIndex = 0;   // Index that marches through 1-D point array
171 
172             // Remember that Y value is the height (the value that we're animating)
173             for (int row = 0; row < _dimension ; row++)
174             {
175                 for (int col = 0; col < _dimension; col++)
176                 {
177                     velocity = -1.0 * _oldBuffer[nPtIndex].Y;     // row, col
178                     smoothed = 0.0;
179 
180                     neighbors = 0;
181                     if (row > 0)    // row-1, col
182                     {
183                         smoothed += _currBuffer[nPtIndex - _dimension].Y;
184                         neighbors++;
185                     }
186 
187                     if (row < (_dimension - 1))   // row+1, col
188                     {
189                         smoothed += _currBuffer[nPtIndex + _dimension].Y;
190                         neighbors++;
191                     }
192 
193                     if (col > 0)          // row, col-1
194                     {
195                         smoothed += _currBuffer[nPtIndex - 1].Y;
196                         neighbors++;
197                     }
198 
199                     if (col < (_dimension - 1))   // row, col+1
200                     {
201                         smoothed += _currBuffer[nPtIndex + 1].Y;
202                         neighbors++;
203                     }
204 
205                     // Will always have at least 2 neighbors
206                     smoothed /= (double)neighbors;
207 
208                     // New height is combination of smoothing and velocity
209                     newHeight = smoothed * SmoothingFactor + velocity;
210 
211                     // Damping
212                     newHeight = newHeight * Damping;
213 
214                     // We write new data to old buffer
215                     Point3D pt = _oldBuffer[nPtIndex];
216                     pt.Y = newHeight;   // row, col
217                     _oldBuffer[nPtIndex] = pt;
218 
219                     nPtIndex++;
220                 }
221             }
222 
223             SwapBuffers();
224         }
225     }
226 }
最后,我們需要掛鈎一切我們的主窗口觸發,我們創建一個WaveGrid實例一些峰值設定在網格中心點當我們開始動畫這個高點回落,引發的海浪

我們在做CompositionTarget.Rendering事件處理程序所有動畫這是推薦的當場WPF中的自定義動畫,而不是一些計時器的Tick事件動畫 Windows Presentation Foundation中如虎添翼,彌敦道470

當你渲染事件處理程序WPF只是繼續無限期渲染一個問題是,該處理程序將調用一個渲染幀原來是太快我們動畫為了獲得我們保持的時間我們最后呈現一個框架,然后等待指定的毫秒呈現另一個軌道

在這里Window1.xaml.cs完整源代碼
 
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Diagnostics;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Windows;
  7 using System.Windows.Controls;
  8 using System.Windows.Data;
  9 using System.Windows.Documents;
 10 using System.Windows.Input;
 11 using System.Windows.Media;
 12 using System.Windows.Media.Media3D;
 13 using System.Windows.Media.Imaging;
 14 using System.Windows.Navigation;
 15 using System.Windows.Shapes;
 16 using System.Windows.Threading;
 17 
 18 namespace WaveSim
 19 {
 20     /// <summary>
 21     /// Interaction logic for Window1.xaml
 22     /// </summary>
 23     public partial class Window1 : Window
 24     {
 25         private Vector3D zoomDelta;
 26 
 27         private WaveGrid _grid;
 28         private bool _rendering;
 29         private double _lastTimeRendered;
 30         private double _firstPeak = 6.5;
 31 
 32         // Values to try:
 33         //   GridSize=20, RenderPeriod=125
 34         //   GridSize=50, RenderPeriod=50
 35         private const int GridSize = 50;   
 36         private const double RenderPeriodInMS = 50;    
 37 
 38         public Window1()
 39         {
 40             InitializeComponent();
 41 
 42             _grid = new WaveGrid(GridSize);        // 10x10 grid
 43             slidPeakHeight.Value = _firstPeak;
 44             _grid.SetCenterPeak(_firstPeak);
 45             meshMain.Positions = _grid.Points;
 46             meshMain.TriangleIndices = _grid.TriangleIndices;
 47 
 48             // On each WheelMouse change, we zoom in/out a particular % of the original distance
 49             const double ZoomPctEachWheelChange = 0.02;
 50             zoomDelta = Vector3D.Multiply(ZoomPctEachWheelChange, camMain.LookDirection);
 51         }
 52 
 53         private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
 54         {
 55             if (e.Delta > 0)
 56                 // Zoom in
 57                 camMain.Position = Point3D.Add(camMain.Position, zoomDelta);
 58             else
 59                 // Zoom out
 60                 camMain.Position = Point3D.Subtract(camMain.Position, zoomDelta);
 61             Trace.WriteLine(camMain.Position.ToString());
 62         }
 63 
 64         // Start/stop animation
 65         private void btnStart_Click(object sender, RoutedEventArgs e)
 66         {
 67             if (!_rendering)
 68             {
 69                 _grid = new WaveGrid(GridSize);        // 10x10 grid
 70                 _grid.SetCenterPeak(_firstPeak);
 71                 meshMain.Positions = _grid.Points;
 72 
 73                 _lastTimeRendered = 0.0;
 74                 CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
 75                 btnStart.Content = "Stop";
 76                 slidPeakHeight.IsEnabled = false;
 77                 _rendering = true;
 78             }
 79             else
 80             {
 81                 CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);
 82                 btnStart.Content = "Start";
 83                 slidPeakHeight.IsEnabled = true;
 84                 _rendering = false;
 85             }
 86         }
 87 
 88         void CompositionTarget_Rendering(object sender, EventArgs e)
 89         {
 90             RenderingEventArgs rargs = (RenderingEventArgs)e;
 91             if ((rargs.RenderingTime.TotalMilliseconds - _lastTimeRendered) > RenderPeriodInMS)
 92             {
 93                 // Unhook Positions collection from our mesh, for performance
 94                 // (see http://blogs.msdn.com/timothyc/archive/2006/08/31/734308.aspx)
 95                 meshMain.Positions = null;
 96 
 97                 // Do the next iteration on the water grid, propagating waves
 98                 _grid.ProcessWater();
 99 
100                 // Then update our mesh to use new Z values
101                 meshMain.Positions = _grid.Points;
102 
103                 _lastTimeRendered = rargs.RenderingTime.TotalMilliseconds;
104             }
105         }
106 
107         private void slidPeakHeight_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
108         {
109             _firstPeak = slidPeakHeight.Value;
110             _grid.SetCenterPeak(_firstPeak);
111         }
112     }
113 }
最終的結果是相當令人滿意的一系列的漣漪初始擾動傳播一個很好的流暢的動畫效果可以安裝和運行模擬點擊這里請注意您可以使用鼠標滾輪放大/縮小
最后,我們需要掛鈎一切我們的主窗口觸發,我們創建一個WaveGrid實例一些峰值設定在網格中心點當我們開始動畫這個高點回落,引發的海浪

我們在做CompositionTarget.Rendering事件處理程序所有動畫這是推薦的當場WPF中的自定義動畫,而不是一些計時器的Tick事件動畫 Windows Presentation Foundation中如虎添翼,彌敦道470

當你渲染事件處理程序WPF只是繼續無限期渲染一個問題是,該處理程序將調用一個渲染幀原來是太快我們動畫為了獲得我們保持的時間我們最后呈現一個框架,然后等待指定的毫秒呈現另一個軌道

在這里Window1.xaml.cs完整源代碼
 
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Diagnostics;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Windows;
  7 using System.Windows.Controls;
  8 using System.Windows.Data;
  9 using System.Windows.Documents;
 10 using System.Windows.Input;
 11 using System.Windows.Media;
 12 using System.Windows.Media.Media3D;
 13 using System.Windows.Media.Imaging;
 14 using System.Windows.Navigation;
 15 using System.Windows.Shapes;
 16 using System.Windows.Threading;
 17 
 18 namespace WaveSim
 19 {
 20     /// <summary>
 21     /// Interaction logic for Window1.xaml
 22     /// </summary>
 23     public partial class Window1 : Window
 24     {
 25         private Vector3D zoomDelta;
 26 
 27         private WaveGrid _grid;
 28         private bool _rendering;
 29         private double _lastTimeRendered;
 30         private double _firstPeak = 6.5;
 31 
 32         // Values to try:
 33         //   GridSize=20, RenderPeriod=125
 34         //   GridSize=50, RenderPeriod=50
 35         private const int GridSize = 50;   
 36         private const double RenderPeriodInMS = 50;    
 37 
 38         public Window1()
 39         {
 40             InitializeComponent();
 41 
 42             _grid = new WaveGrid(GridSize);        // 10x10 grid
 43             slidPeakHeight.Value = _firstPeak;
 44             _grid.SetCenterPeak(_firstPeak);
 45             meshMain.Positions = _grid.Points;
 46             meshMain.TriangleIndices = _grid.TriangleIndices;
 47 
 48             // On each WheelMouse change, we zoom in/out a particular % of the original distance
 49             const double ZoomPctEachWheelChange = 0.02;
 50             zoomDelta = Vector3D.Multiply(ZoomPctEachWheelChange, camMain.LookDirection);
 51         }
 52 
 53         private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
 54         {
 55             if (e.Delta > 0)
 56                 // Zoom in
 57                 camMain.Position = Point3D.Add(camMain.Position, zoomDelta);
 58             else
 59                 // Zoom out
 60                 camMain.Position = Point3D.Subtract(camMain.Position, zoomDelta);
 61             Trace.WriteLine(camMain.Position.ToString());
 62         }
 63 
 64         // Start/stop animation
 65         private void btnStart_Click(object sender, RoutedEventArgs e)
 66         {
 67             if (!_rendering)
 68             {
 69                 _grid = new WaveGrid(GridSize);        // 10x10 grid
 70                 _grid.SetCenterPeak(_firstPeak);
 71                 meshMain.Positions = _grid.Points;
 72 
 73                 _lastTimeRendered = 0.0;
 74                 CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
 75                 btnStart.Content = "Stop";
 76                 slidPeakHeight.IsEnabled = false;
 77                 _rendering = true;
 78             }
 79             else
 80             {
 81                 CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);
 82                 btnStart.Content = "Start";
 83                 slidPeakHeight.IsEnabled = true;
 84                 _rendering = false;
 85             }
 86         }
 87 
 88         void CompositionTarget_Rendering(object sender, EventArgs e)
 89         {
 90             RenderingEventArgs rargs = (RenderingEventArgs)e;
 91             if ((rargs.RenderingTime.TotalMilliseconds - _lastTimeRendered) > RenderPeriodInMS)
 92             {
 93                 // Unhook Positions collection from our mesh, for performance
 94                 // (see http://blogs.msdn.com/timothyc/archive/2006/08/31/734308.aspx)
 95                 meshMain.Positions = null;
 96 
 97                 // Do the next iteration on the water grid, propagating waves
 98                 _grid.ProcessWater();
 99 
100                 // Then update our mesh to use new Z values
101                 meshMain.Positions = _grid.Points;
102 
103                 _lastTimeRendered = rargs.RenderingTime.TotalMilliseconds;
104             }
105         }
106 
107         private void slidPeakHeight_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
108         {
109             _firstPeak = slidPeakHeight.Value;
110             _grid.SetCenterPeak(_firstPeak);
111         }
112     }
113 }
最終的結果是相當令人滿意的一系列的漣漪初始擾動傳播一個很好的流暢的動畫效果可以安裝和運行模擬點擊這里請注意您可以使用鼠標滾輪放大/縮小


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM