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,本篇就说到这里了,开饭了。
来源: http://www.cnblogs.com/tcjiaan/p/7821066.html