天天看點

Silverlight C# 遊戲開發:L8 材質和貼圖

對于3D來說,主要的組成要素是:模型、燈光、錄影機,模型中的貼圖部分不容忽視,貼圖為模型帶來生命力,在遊戲開發當中非常重要,了解貼圖的方式可以幫助我們作出,這一篇主要是說有關于貼圖方面的内容。下面是今天的執行個體:

<a target="_blank" href="http://blog.51cto.com/attachment/201111/121704803.jpg"></a>

記得有一位朋友曾經說過,3D世界總會回歸到2D當中,此話很有道理,無論我們怎麼建構3D世界仍然還隻是在面前的螢幕上顯示,而這個就是典型的3D到2D,其實,我們都被欺騙了,所有看到的都不是真正意義上的3D世界,是通過計算出來的結果,偉大的數學在這裡發揮到了極緻,就如Balder就是典型的通過計算的方式将3D控件渲染到Silverlight的平面上,隻要對3D數學有一定的了解,都可以作出一個3D世界,說了這些有什麼用呢?和貼圖有很大的關系,大部分的開發者可能隻是知道“貼”卻對其原理一知半解,貼圖的英文一般稱之是Texture,貼圖僅僅是另外一個子集下面的一個部分,而這個更大的集合是材質Material,要知道,世間萬物并非僅僅隻有紋理,它包含了諸如漫反射(diffuse)和反射(reflection)的屬性,是以你仔細看某一個事物的時候,在不同的時空看到的結果不是一樣。如果讨論3D世界,我想可能也非一時之事,今天咱們就之說說材質。

下面展示了一個3D世界中的典型材質賦予方式:

<a target="_blank" href="http://blog.51cto.com/attachment/201111/121717258.jpg"></a>

在Balder當中,Balder.Objects.Geometries對象都帶有Material屬性,隻要對這個屬性進行設定即可。

material = new Material();  

Balder.Imaging.Image image = Runtime.Instance.ContentManager.Load&lt;Balder.Imaging.Image&gt;("/Balder_Studio;component/Res/map01.jpg");  

material.DiffuseMap = new ImageMap(image) ;  

material.DiffuseMapOpacity = 1;  

material.Opacity = 1;  

material.DoubleSided = true;  

上面的代碼是建立一個基本的材質,然後從資源中讀取一個貼圖給Diffuse上,上次有位朋友問我如何在Balder中讀取一個圖檔,其方式就是上面的方式,需要提醒的是,現在這種方法隻能讀取自身工程的檔案,如果是外部的話,就會報錯。

好吧,其實今天所有的核心就在上面的幾行代碼當中,下面僅僅是做了控制,現在我們弄一個UI控制界面,來操作材質和貼圖使我們更加直覺。

<a target="_blank" href="http://blog.51cto.com/attachment/201111/121746709.jpg"></a>

為了友善起見,這次用上了屬性綁定,界面的XAML如下:

&lt;UserControl 

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

    xmlns:local="clr-namespace:Balder_Studio" x:Class="Balder_Studio.Lesson08" 

    mc:Ignorable="d" 

    d:DesignHeight="441" d:DesignWidth="550"&gt;      

    &lt;Grid&gt;          

        &lt;Grid x:Name="LayoutRoot" Background="White" d:IsLocked="True"/&gt; 

        &lt;StackPanel Margin="12,8,0,0" HorizontalAlignment="Left" Height="218" VerticalAlignment="Top"&gt; 

            &lt;StackPanel Margin="0" Orientation="Horizontal" Visibility="Collapsed"&gt; 

                &lt;TextBlock Margin="0,9,0,0" FontSize="12" Width="118"&gt;&lt;Run Text="貼圖透明度"/&gt;&lt;/TextBlock&gt; 

                &lt;Slider x:Name="SliderOpacity" Value="1" Width="140" Maximum="1" LargeChange="0.2"/&gt; 

                &lt;TextBlock x:Name="TextValue" Text="{Binding Value, ElementName=SliderOpacity}" Margin="0,9,0,0" FontSize="12" Width="71"/&gt; 

            &lt;/StackPanel&gt; 

            &lt;StackPanel Height="28" Orientation="Horizontal" VerticalAlignment="Bottom"&gt; 

                &lt;TextBlock Margin="0,8,0,0" FontSize="12" Width="118" Text="貼圖"/&gt; 

                &lt;ComboBox x:Name="DiffuseMapSel" Width="140"&gt; 

                &lt;/ComboBox&gt; 

                &lt;Button Content="清理貼圖" Width="60" Click="Button_Click_ClearDiffuseMap"&gt;&lt;/Button&gt; 

            &lt;StackPanel Margin="0" Orientation="Horizontal"&gt; 

                &lt;TextBlock Margin="0,9,0,0" FontSize="12" Width="118"&gt;&lt;Run Text="貼圖"/&gt;&lt;Run Text="透明度"/&gt;&lt;/TextBlock&gt; 

                &lt;Slider x:Name="SliderDOpacity" Width="140" Maximum="1" Value="1" LargeChange="0.2"/&gt; 

                &lt;TextBlock Text="{Binding Value, ElementName=SliderDOpacity}" Margin="0,9,0,0" FontSize="12" Width="71"/&gt; 

                &lt;TextBlock Margin="0,8,0,0" FontSize="12" Width="118" Text="反射貼圖"/&gt; 

                &lt;ComboBox x:Name="ReflectionMapSel" Width="140"/&gt; 

                &lt;Button Content="清理貼圖" Width="60" Click="Button_Click_ClearReflectionMap"&gt;&lt;/Button&gt; 

                &lt;TextBlock Margin="0,9,0,0" FontSize="12" Width="118"&gt;&lt;Run Text="反射"/&gt;&lt;Run Text="貼圖"/&gt;&lt;Run Text="透明度"/&gt;&lt;/TextBlock&gt; 

                &lt;Slider x:Name="SliderROpacity" Width="140" Maximum="1" Value="1" LargeChange="0.2"/&gt; 

                &lt;TextBlock Text="{Binding Value, ElementName=SliderROpacity}" Margin="0,9,0,0" FontSize="12" Width="71"/&gt; 

        &lt;/StackPanel&gt;          

    &lt;/Grid&gt; 

&lt;/UserControl&gt; 

然後背景代碼如下,做了簡單的注釋,相信各位高手會明白的。

背景代碼 

//////////////////////////////////////////////////  

// Silvery Night  

// Nowpaper原創Silverlight Balder3D文章,共同研究和探讨:)  

//   

// http://www.nowpaper.net  

// http://www.cnblogs.com/nowpaper  

using System;  

using System.Windows.Threading;  

using System.Windows.Controls;  

using System.Windows.Media;  

using System.Windows.Media.Imaging;  

using Balder.Math;  

using Balder.Objects.Geometries;  

using Balder.View;  

using Balder.Lighting;  

using Balder.Execution;  

using System.Windows;  

using System.Windows.Resources;  

using Balder.Materials;  

using Balder.Assets;  

using System.Windows.Data;  

using System.Collections.Generic;  

namespace Balder_Studio  

{  

    public partial class Lesson08 : UserControl  

    {  

        //Heightmap  

        Heightmap heightmap = new Heightmap();  

        Camera camera = new Camera();  

        //材質  

        Material material = new Material();  

        //貼圖檔案清單  

        public List&lt;string&gt; ImageList = new List&lt;string&gt;()  

        {  

            "/Balder_Studio;component/Res/map01.jpg",  

            "/Balder_Studio;component/Res/map02.jpg",  

            "/Balder_Studio;component/Res/map03.jpg",  

            "/Balder_Studio;component/Res/map04.jpg"  

        };  

        public Lesson08()  

            InitializeComponent();  

            InitializeUILogic();  

            //L1  

            Game game = new Game() { Width = 600, Height = 400 };  

            game.Camera = camera;  

            game.Camera.Position = new Coordinate(100, 120, 150);  

            game.Camera.Target = new Coordinate(0, 0, 0);  

            game.Children.Add(new OmniLight()  

            {  

                Position = new Coordinate(0, 100, 0),  

                Ambient = Colors.Transparent,  

                Specular = Colors.Transparent,  

                Diffuse = Colors.Transparent,  

                Strength = 0.55,  

            });  

            //L3  

            Game_Axis axis_x = new Game_Axis(new Vertex(-300, 0, 0), new Vertex(300, 0, 0), Colors.Red);  

            Game_Axis axis_y = new Game_Axis(new Vertex(0, -300, 0), new Vertex(0, 300, 0), Colors.Blue);  

            Game_Axis axis_z = new Game_Axis(new Vertex(0, 0, -300), new Vertex(0, 0, 300), Colors.Green);  

            game.Children.Add(axis_x);  

            game.Children.Add(axis_y);  

            game.Children.Add(axis_z);  

            heightmap.Dimension = new Dimension() { Width = 128, Height = 128 };  

            heightmap.LengthSegments = 2;  

            heightmap.HeightSegments = 2;  

            heightmap.InteractionEnabled = true;  

            //L8  

            //從位圖中建立高度圖  

            CreateHeightMapFormBitmap(new Uri("/Balder_Studio;component/Res/heightmap.jpg", UriKind.Relative));  

            //通過ContentManager讀取指定貼圖  

            Balder.Imaging.Image image = Runtime.Instance.ContentManager.Load&lt;Balder.Imaging.Image&gt;("/Balder_Studio;component/Res/map01.jpg");  

            //漫反射貼圖  

            material.DiffuseMap = new ImageMap(image) ;  

            //漫射貼圖透明度  

            material.DiffuseMapOpacity = 1;  

            //材質透明度  

            material.Opacity = 1;  

            //雙面材質  

            material.DoubleSided = true;  

            //将材質附上  

            heightmap.Material = material;  

            game.Children.Add(heightmap);  

            LayoutRoot.Children.Add(game);  

            this.KeyDown += new System.Windows.Input.KeyEventHandler(Lesson08_KeyDown);              

        }  

        //初始化UI  

        private void InitializeUILogic()  

        {              

            foreach (var item in ImageList)  

                Image image = new Image() { Width = 128, Height = 128 };  

                Image image1 = new Image() { Width = 128, Height = 128 };  

                imageimage1.Source = image.Source = new BitmapImage(new Uri(item, UriKind.Relative));  

                DiffuseMapSel.Items.Add(image);  

                ReflectionMapSel.Items.Add(image1);  

            }  

            DiffuseMapSel.SelectionChanged += new SelectionChangedEventHandler(MapSel_SelectionChanged);  

            ReflectionMapSel.SelectionChanged += new SelectionChangedEventHandler(MapSel_SelectionChanged);  

            SliderOpacity.ValueChanged += new RoutedPropertyChangedEventHandler&lt;double&gt;(SliderOpacity_ValueChanged);  

            SliderDOpacity.ValueChanged += new RoutedPropertyChangedEventHandler&lt;double&gt;(SliderOpacity_ValueChanged);  

            SliderROpacity.ValueChanged += new RoutedPropertyChangedEventHandler&lt;double&gt;(SliderOpacity_ValueChanged);  

        }          

        void MapSel_SelectionChanged(object sender, SelectionChangedEventArgs e)  

            var comboBox = sender as ComboBox;  

            if(comboBox.SelectedIndex == -1)  

                return;  

            var filename = ImageList[comboBox.SelectedIndex];  

            Balder.Imaging.Image image;  

            image = Runtime.Instance.ContentManager.Load&lt;Balder.Imaging.Image&gt;(filename);  

            if (sender == DiffuseMapSel)  

                material.DiffuseMap = new ImageMap(image);  

            else  

                if (ReflectionMapSel == sender)   

                {  

                    material.ReflectionMap = new ImageMap(image);  

                }  

        }         

        void SliderOpacity_ValueChanged(object sender, RoutedPropertyChangedEventArgs&lt;double&gt; e)  

            if (sender == SliderOpacity)  

                material.Opacity = e.NewValue;  

                if (sender == SliderDOpacity)  

                    material.DiffuseMapOpacity = e.NewValue;  

                else  

                    if (sender == SliderROpacity)  

                    {  

                        material.ReflectionMapOpacity = e.NewValue;  

                    }  

        void Lesson08_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)  

            switch (e.Key)  

                case System.Windows.Input.Key.W:  

                        var c = camera.Target - camera.Position;  

                        camera.Position += c / 10;  

                        camera.Target += c / 10;  

                    break;  

                case System.Windows.Input.Key.S:  

                        camera.Position -= c / 10;  

                        camera.Target -= c / 10;  

        //從位圖中建立高度圖  

        void CreateHeightMapFormBitmap(Uri uri)  

            BitmapImage bitmap = new BitmapImage();  

            //從資源中取得BitmapStream  

            StreamResourceInfo sri = Application.GetResourceStream(uri);  

            bitmap.SetSource(sri.Stream);  

            //生成WriteableBitmap  

            WriteableBitmap writeablebitmap = new WriteableBitmap(bitmap);  

            //建立高度圖數組  

            float[,] HeightmapArray = new float[bitmap.PixelHeight, bitmap.PixelWidth];  

            //将數組拷貝到高度圖數組  

            for (int i = 0; i &lt; bitmap.PixelHeight; i++)  

                for (int j = 0; j &lt; bitmap.PixelWidth; j++)  

                    int index = bitmap.PixelWidth * i + j;  

                    int pixel = writeablebitmap.Pixels[index];  

                    byte[] bytes = BitConverter.GetBytes(pixel);  

                    //計算:顔色越深則越低,顔色月淺則越高,50是最高的高度值  

                    HeightmapArray[i, j] = ((float)(bytes[0] + bytes[1] + bytes[2]) / 3) / 255 * 50;  

            //指派  

            heightmap.HeightmapArray = HeightmapArray;  

        private void Button_Click_ClearDiffuseMap(object sender, RoutedEventArgs e)  

            heightmap.Material.DiffuseMap = null;  

        private void Button_Click_ClearReflectionMap(object sender, RoutedEventArgs e)  

            heightmap.Material.ReflectionMap = null;  

    }  

}  

下一篇我們介紹材質的應用,如何對模型進行貼圖和更換貼圖,以及貼圖的屬性,同時可能還結合HeightMap依據地理資訊制作真實地圖,那麼下次再見。

本文轉自nowpaper 51CTO部落格,原文連結:http://blog.51cto.com/nowpaper/710920