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 属性,用以指定光的颜色,默认是白光。当物体被白光照亮时,它呈现的是本色(本来面目)。所以,上面代码的执行效果如下图。

Intensity 表示光照强度,从上面的例子咱们看到,这个值应该大于 0,小于等于 0 就全黑了,什么都看不见,那就没有意义了,值也不要太大,所以我这个例子最大就到 10 ,当然你可以设置 100、1000,可是强度太大了,会亮瞎眼的,什么也看不见,也是没有意义的。光照强度默认是 1 ,我们可以根据需要设置合适的值。

我们还可以换一下其他颜色的光,比如,我们改一下代码,用充满幽灵意味的绿光去照射一下。

  light.Color = Colors.Green;

然后,效果很惊人。

定点光

点光,即 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。

来,看看效果吧。

锥光

这种光源类似手电筒的光,其实与上面的 Pointlight 很像,但锥光带有内圈和外圈。所以,锥光也有颜色、强度、衰减程度等参数,当然也会有位置。

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

InnerConeIntensity 表示内圈的光线强度,OuterConeIntensity 表示外圈的光线强度。

Offset 表示光的位置,和上面的定点光类似,但锥光多了个 Direction 属性。用过手电你都知道的,它有个照射方向。如果光源位于物体前方,要想让它照亮物体,Z轴上的方向必须是负值,只有负值才会照进屏幕里面;如果光源在物体后面,Z轴上的方向当然要正值,这样照射方向才会指向屏幕外。

我们做个例子。在界面上放一张图,先给大家看看原图。

这书房是不是很高大上呢。然后我们让它在 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);
}

好了,看看效果吧。

OK,本篇就说到这里了,开饭了。

【Win 10 应用开发】UI Composition 札记(五):灯光的更多相关文章

  1. 【Win 10 应用开发】UI Composition 札记(一):视图框架的实现

    在开始今天的内容之前,老周先说一个问题,这个问题记得以前有人提过的. 设置 Windows.ApplicationModel.Core.CoreApplicationView.TitleBar.Ext ...

  2. 【Win 10 应用开发】UI Composition 札记(三):与 XAML 集成

    除了 DirectX 游戏开发,我们一般很少单独使用 UI Composition ,因此,与 XAML 互动并集成是必然结果.这样能够把两者的优势混合使用,让UI布局能够更灵活. 说到与 XAML ...

  3. 【Win 10 应用开发】UI Composition 札记(二):基本构件

    在上一篇中,老周用一个示例,演示了框架视图的创建过程,在本篇中,老周将给大伙伴们说一下 Composition 构建 UI 的一些“零件”. UI Composition 有一个核心类——对,就是 C ...

  4. 【Win 10 应用开发】UI Composition 札记(四):绘制图形

    使用 Win 2D 组件,就可以很轻松地绘制各种图形,哪怕你没有 D2D 相关基础,也不必写很复杂的 C++ 代码. 先来说说如何获取 Win 2D 组件.很简单,创建 UWP 应用项目后,你打开“解 ...

  5. 【Win 10 应用开发】UI Composition 札记(六):动画

    动画在 XAML 中也有,而且基本上与 WPF 中的用法一样.不过,在 UWP 中,动画还有一种表现方式—— 通过 UI Composition 来创建. 基于 UI Composition 的动画, ...

  6. 【Win 10 应用开发】启动远程设备上的应用

    这个功能必须在“红石-1”(build 14393)以上的系统版中才能使用,运行在一台设备上的应用,可以通过URI来启动另一台设备上的应用.激活远程应用需要以下前提: 系统必须是build 14393 ...

  7. 【Win 10应用开发】认识一下UAP项目

    Windows 10 SDK预览版需要10030以上版本号的Win 10预览版系统才能使用.之前我安装的9926的系统,然后安装VS 2015 CTP 6,再装Win 10 SDK,但是在新建项目后, ...

  8. 【Win 10 应用开发】在代码中加载文本资源

    记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...

  9. 【Win 10 应用开发】导入.pfx证书

    这个功能其实并不常用,一般开发较少涉及到证书,不过,简单了解一下还是有必要的. 先来说说制作测试证书的方法,这里老周讲两种方法,可以生成用于测试的.pfx文件. 产生证书,大家都知道有个makecer ...

随机推荐

  1. Dubbo服务集群、服务启动依赖检查

    一.什么叫Dubbo服务集群 指把同一个服务部署到多台机器,然后通过Dubbo服务集群的容错配置实现一台机器的服务挂掉之后自动切换到另外的一台机器 二.Dubbo服务集群容错配置--集群容错模式 标签 ...

  2. Java历程-初学篇 Day04选择结构(1)

    一,if 1,单分支 if(条件){ } 示例: 2,双分支 if(条件){ }else{ } 示例: 3,多重if if(条件){ }else if(条件){ }else{ } 示例: 4,嵌套if ...

  3. web前端面试官挖的那些坑(js)

    题目1: function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () ...

  4. IDL Interpolate()函数

    Interpolate()函数:可以将数组调整到同维任意大小,并支持任意定位差值.调用格式为 Interpolate(数组,X[,Y[,Z]][,关键字]) 例子: IDL> arr=findg ...

  5. gitlab实时备份方案(非官方命令)

    gitlab自带的备份功能做不到实时备份,为了尽可能减少意外情况导致的丢失数据,自己搞了一个实时备份的功能. 备份的大头主要是两部分,数据库和代码库.数据库由DBA配置主备. 仓库经过测试,通过lsy ...

  6. 由String的构造方法引申出来的java字符编码

    在String类的constructors中,有一个constructor是将int数组类型转化为字符串: int[] num = {48,49,50,51,52}; String numStr = ...

  7. 在 Tomcat 8 部署多端口项目

    一般的部署途径 Tomcat 的部署途径很多,一般有如下几种: 直接将 War 包拷贝到 webapps 目录中,然后启动 Tomcat. 登陆 Tomcat 管理控制台http://localhos ...

  8. javaScript基础的基础

    JavaScript是一个脚本语言,需要有宿主文件,他的宿主文件是HTML文件. 与JAVA没有直接关系 一般写在 1.head里面 2.body里面 3.</html>后面 一般写在&l ...

  9. TensorFlow问题:The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.

    1. 问题描述 The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available o ...

  10. Hadoop(九)Hadoop IO之Compression和Codecs

    前言 前面一篇介绍了Java怎么去查看数据块的相关信息和怎么去查看文件系统.我们只要知道怎么去查看就行了!接下来我分享的是Hadoop的I/O操作. 在Hadoop中为什么要去使用压缩(Compres ...