天天看點

【Win 10 應用開發】UI Composition 劄記(五):燈光

UI Composition 除了能夠為 UI 元素建立三維空間外,還有相當重要的一個部件——燈光。宇宙萬物的精彩缤紛,皆源于光明,光,使我們看到各種東西,除了黑洞之外的世界都是五彩斑讕的。故而,真要模拟現實物體,合理的燈光照射是很關鍵,不然就“不像”了。

Composition API 為各種燈光效果提煉了一個公共基類——CompositionLight,它帶有兩個規範性的屬性:

Targets:可視化元素的集合。用來确定場景中哪些東西應該被照亮。比如,你模拟了一面牆,牆壁上挂着各種畫,有山水,有鳥獸,有美女,有蝙蝠,如果你要看畫,黑乎乎的你連根狗毛也看不見的,是以你看到很多美術館或博物館都會安裝各種燈源,隻有打燈你才能看到這些畫的。如果你希望看美女,那麼就把美女加入 Targets 集合,這樣美女就會被燈光照亮。

ExclusionsFromTargets:這是一個排除項清單。與上面的剛好反過來,就是指定你不希望被照亮的物體。如果你覺得蝙蝠太猙獰太恐怖,不想看,你可以把它排除掉,就不會被燈光照亮了。

環境光

環境光類似于咱們家裡的白熾光、節能燈等,這種光源比較均勻,基本可以把整個房間照亮。

我們看一個環境光的例子。下面示例,在界面上加載一張圖檔,然後我們用環境光去照亮它。順便放一個 Slider 控件,目的是可以調節光照的強度。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Image Source="Assets/5.jpg" Stretch="Uniform" Name="img"/>
        <Slider Grid.Row="1" Margin="2,9" StepFrequency="0.1" Value="1" Minimum="0" Maximum="10" ValueChanged="OnSliderValChanged"/>
    </Grid>      

切換到代碼檔案,在頁面類的構造函數中,咱們添加一下燈光效果。

AmbientLight light = null;
        public MainPage()
        {
            this.InitializeComponent();

            Visual v = ElementCompositionPreview.GetElementVisual(img);
            Compositor compos = v.Compositor;
            light = compos.CreateAmbientLight();
            light.Targets.Add(v);

        }      

注意,我為什麼要把 AmbientLight 的變量聲明到類級别呢,因為可以在後面調整它的強度。下面是 Slider 控件的 ValueChanged 事件的處理代碼。

private void OnSliderValChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            if(light != null)
            {
                light.Intensity = (float)e.NewValue;
            }
        }      

這裡要先判斷一下 light 變量是否為 null,因為這個事件處理是在 XAML 代碼中關聯的,即在頁面類執行個體構造過程中會調用這個方法(主要是設定 Value 屬性的值時發生),那個時候,環境光對象還沒有建立,如果不判斷,就會出現 null 引用異常。

AmbientLight 類表示環境光,它有一個 Color 屬性,用以指定光的顔色,預設是白光。當物體被白光照亮時,它呈現的是本色(本來面目)。是以,上面代碼的執行效果如下圖。

【Win 10 應用開發】UI Composition 劄記(五):燈光

Intensity 表示光照強度,從上面的例子咱們看到,這個值應該大于 0,小于等于 0 就全黑了,什麼都看不見,那就沒有意義了,值也不要太大,是以我這個例子最大就到 10 ,當然你可以設定 100、1000,可是強度太大了,會亮瞎眼的,什麼也看不見,也是沒有意義的。光照強度預設是 1 ,我們可以根據需要設定合适的值。

我們還可以換一下其他顔色的光,比如,我們改一下代碼,用充滿幽靈意味的綠光去照射一下。

light.Color = Colors.Green;      

然後,效果很驚人。

【Win 10 應用開發】UI Composition 劄記(五):燈光

定點光

點光,即 PointLight,它就像一盞小燈泡,發出的光并不能像環境光那樣覆寫全面,而是點狀的,但它可以照亮四周的物體,而且距離物體近的話,照得更亮,這就很像火把、蠟燭。是以,PointLight 類的屬性會比環境光多一些,也複雜一些。

Color 和 Intensity 屬性是一樣的,前者表示燈光的顔色,後者表示強度。除此之外,還有以下這幾個:ConstantAttenuation、QuadraticAttenuation、LinearAttenuation,這幾個屬性的性質是一樣的,隻是算法不同,有的是平方值的,有的是線性的。這些值是用來設定光的衰減速度,啥意思呢,我們剛剛不是說過嗎,點狀光的照亮程度是跟距離有關,随着燈光與物體的距離增大,亮度會衰減。當然,如果光線很強的情況下,距離遠可能照亮的範圍更大,近距離情況下,會把局部照得更亮。這幾個值就是用來描述光線衰減的速度。在現實世界中,這可能與空氣能見度或空氣密度有關,因為這些要素會影響光的傳播。但在虛拟圖形中不存在真實的大氣,是以需要通過算法來模拟。

由于點狀光是一個發光點,是以它肯定會有位置的,即坐标,下面兩個屬性用來确定點狀光的坐标:Offset 屬性确定位置,它是一個三維坐标;CoordinateSpace 又是啥呢,它要求指定一個可視化對象,用來計算光照的強度的。你想啊,大晚上,你在一片荒野上點根火把,你會覺得這火把好像不怎麼亮,但是,如果你在一個狹窄的山洞裡面點一根火把,你就會覺得它特别亮。是以,這個屬性就是設定一個容器,好确定這點光到底能照多亮。

下面我們看看定點光的例子。

在界面上我們放置一個文本,然後,下面的 Slider 控件用來調整點光的衰減速度,即  ConstantAttenuation 屬性,這個值越大,表明同樣距離下燈光會更弱,因為它衰減得更快更明顯,這個值是大于0的任意值。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Border Background="Black" Grid.Row="0" Name="bd">
            <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="歡迎觀臨" FontSize="150" FontFamily="華文行楷" Foreground="Gold" Name="text"/>
        </Border>

        <Slider Grid.Row="1" Margin="2,7" Maximum="5" Minimum="1" Value="1" StepFrequency="0.1" Name="sld"/>
    </Grid>      

TextBlock 為什麼要放到一個 Border 中呢,前面說了,定點光需要一個容器來計算照亮程度,是以,Border 是用來作為參考容器的。

切換到代碼視圖,在頁面類的構造函數中,我們來加一下定點光。

public MainPage()
        {
            this.InitializeComponent();

            // 擷取容器
            Visual vsContainer = ElementCompositionPreview.GetElementVisual(bd);
            // 擷取 TextBlock 的可視化對象
            Visual txtVisual = ElementCompositionPreview.GetElementVisual(text);
            Compositor compos = vsContainer.Compositor;
            // 建立光源
            PointLight light = compos.CreatePointLight();
            // 燈光顔色
            light.Color = Colors.Silver;
            // 強度
            light.Intensity = 3.6f;
            // 位置
            light.Offset = new Vector3(500f, 280f, 45f);
            // 照射目标
            light.Targets.Add(txtVisual);
            // 相對容器
            light.CoordinateSpace = vsContainer;

            // 處理 ValueChanged 事件
            sld.ValueChanged += (k, x) =>
            {
                light.ConstantAttenuation = (float)sld.Value;
            };
        }      

這一回處理 ValueChanged 事件就不需要判斷 light 是否為null了,因為附加這個事件處理時,light 對象已經初始化。

注意,這裡我們不僅要擷取 TextBlock 的Visual ,盡管我們的照亮目标是它,但是,因為這種光源需要容器,是以我們要同時獲得 Border 的 Visual。

來,看看效果吧。

【Win 10 應用開發】UI Composition 劄記(五):燈光

錐光

這種光源類似手電筒的光,其實與上面的 Pointlight 很像,但錐光帶有内圈和外圈。是以,錐光也有顔色、強度、衰減程度等參數,當然也會有位置。

InnerConeAngle 是内圈的角度,OuterConeAngle 是外圈的角度,用弧度角表示。如果想用角度,可以用 InnerConeAngleInDegrees 和 OuterConeAngleInDegrees 屬性。

InnerConeIntensity 表示内圈的光線強度,OuterConeIntensity 表示外圈的光線強度。

Offset 表示光的位置,和上面的定點光類似,但錐光多了個 Direction 屬性。用過手電你都知道的,它有個照射方向。如果光源位于物體前方,要想讓它照亮物體,Z軸上的方向必須是負值,隻有負值才會照進螢幕裡面;如果光源在物體後面,Z軸上的方向當然要正值,這樣照射方向才會指向螢幕外。

我們做個例子。在界面上放一張圖,先給大家看看原圖。

【Win 10 應用開發】UI Composition 劄記(五):燈光

這書房是不是很高大上呢。然後我們讓它在 Image 元素上加載。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Border Name="bd" Background="Black">
            <Image Name="img" Source="Assets/2.jpg"/>
        </Border>
    </Grid>      

Image 元素外面也需要一個容器,這裡我還是用Border,因為錐光和定點光一樣,需要一個容器來計算光照。

定位到代碼檔案,在頁面類的構造函數中添加光源。

public MainPage()
        {
            this.InitializeComponent();

            // 擷取目标元素與容器元素
            Visual container = ElementCompositionPreview.GetElementVisual(bd);
            Visual vimg = ElementCompositionPreview.GetElementVisual(img);
            // 建立光源
            SpotLight light = vimg.Compositor.CreateSpotLight();
            // 設定容器
            light.CoordinateSpace = container;
            // 添加照亮目标
            light.Targets.Add(vimg);
            // 外圈和内圈光線的顔色
            light.OuterConeColor = Colors.Blue;
            light.InnerConeColor = Colors.LightYellow;
            // 外圈和内圈光線的強度
            light.InnerConeIntensity = 3.2f;
            light.OuterConeIntensity = 1f;
            // 角度
            light.InnerConeAngleInDegrees = 30f;
            light.OuterConeAngleInDegrees = 90f;
            // 位置
            light.Offset = new Vector3(550f, 270f, 150f);
            // 方向
            light.Direction = new Vector3(-1f, 1.1f, -1f);
        }      

好了,看看效果吧。

【Win 10 應用開發】UI Composition 劄記(五):燈光

 OK,本篇就說到這裡了,開飯了。