here is a relief shader for WPF

N

not_a_commie

I coded a relief shader to test out the shader support in WPF. Here is
the code if anyone is interested. You will need to find your own
height map image to test it with (and change the max width/height in
the XAML file).

First, the fx file:

sampler2D implicitInput : register(s0);
float angle : register(c0);
float widthr : register(c1); // reciprocal of the width
float heightr : register(c2);

float4 main(float2 uv : TEXCOORD) : COLOR
{
// we range 0 to 1 on both width and height
// that means we need a square image unless we have the width and
height
float2 vec = float2(cos(angle)*widthr, sin(angle)*heightr);
float4 color1 = tex2D(implicitInput, uv);
float4 color2 = tex2D(implicitInput, uv + vec);
float gray1 = color1.r * 0.3 + color1.g * 0.59 + color1.b * 0.11;
float gray2 = color2.r * 0.3 + color2.g * 0.59 + color2.b * 0.11;
float diff = gray1 - gray2;
// let's scale the current shade down and add that
gray1 *= 0.1f;
float4 ret = float4(0.45f + gray1 + diff, 0.45f + gray1 + diff,
0.45f + gray1 + diff, 1.0f);
ret = saturate(ret);
return ret;
}

Now the XAML file:

<Window x:Class="WpfReleif.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Relief Map" Height="400" Width="800">
<DockPanel>
<StatusBar DockPanel.Dock="Bottom">
<Label Content="Angle:"/>
<Slider Name="angSlider" Minimum="0" Maximum="360" Value="0"
Width="200" SmallChange="1" LargeChange="5" />
<Label Content="{Binding Value, ElementName=angSlider}"/>
</StatusBar>
<Image Name="image" Source="nearby.png" MaxWidth="1204"
MaxHeight="776"/>
</DockPanel>
</Window>

And now the CS file associated with the XAML:

using System;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Media.Effects;

namespace WpfReleif
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1
{
public Window1()
{
InitializeComponent();
var re = new ReliefEffect();
image.Effect = re;
angSlider.ValueChanged += (sender, e) => re.Angle = (float)
(angSlider.Value / 180.0 * Math.PI);
image.SizeChanged += (sender, e) => { re.WidthR = (float)(1.0 /
image.ActualWidth); re.HeightR = (float)(1.0 / image.ActualHeight); };
}

public class ReliefEffect: ShaderEffect
{
private static readonly PixelShader _shader;
static ReliefEffect()
{
var sdk = Environment.GetEnvironmentVariable("DXSDK_DIR");
if (sdk == null)
throw new InvalidOperationException("Expecting environment var
DXSDK_DIR: install a new version of the DirectX SDK");
var proc = Environment.GetEnvironmentVariable
("PROCESSOR_ARCHITECTURE");
if (proc == null)
proc = "x86";
var path = Path.Combine(sdk, "Utilities" +
Path.DirectorySeparatorChar + "Bin" + Path.DirectorySeparatorChar +
proc + Path.DirectorySeparatorChar + "fxc.exe");
var psi = new ProcessStartInfo(path);
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
var fn = Path.GetTempFileName();
psi.Arguments = string.Format("/T ps_2_0 /E main /Fo\"{0}\"
\"{1}\"", fn, "ReliefShader.fx"); // they change the filename case
using (var p = Process.Start(psi))
{
var error = p.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(error))
throw new InvalidOperationException(error);
p.WaitForExit(); // just in case our read returns early for some
reason
}
_shader = new PixelShader { UriSource = new Uri(fn)};
// TODO: see if you can delete this after creating obj
}

public ReliefEffect()
{
PixelShader = _shader;
UpdateShaderValue(AngleProperty);
UpdateShaderValue(WidthRProperty);
UpdateShaderValue(HeightRProperty);
}

public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(float), typeof
(ReliefEffect), new UIPropertyMetadata(0.0f,
PixelShaderConstantCallback(0)));
public float Angle
{
get { return (float)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}

public static readonly DependencyProperty WidthRProperty =
DependencyProperty.Register("WidthR", typeof(float), typeof
(ReliefEffect), new UIPropertyMetadata(1.0f,
PixelShaderConstantCallback(1)));
public float WidthR
{
get { return (float)GetValue(WidthRProperty); }
set { SetValue(WidthRProperty, value); }
}

public static readonly DependencyProperty HeightRProperty =
DependencyProperty.Register("HeightR", typeof(float), typeof
(ReliefEffect), new UIPropertyMetadata(1.0f,
PixelShaderConstantCallback(2)));
public float HeightR
{
get { return (float)GetValue(HeightRProperty); }
set { SetValue(HeightRProperty, value); }
}

#region Implementation of IDisposable

#endregion
}

}
}
 
N

not_a_commie

So this seems to work better without the saturate call:

sampler2D implicitInput : register(s0);
float angle : register(c0);
float widthr : register(c1); // reciprocal of the width
float heightr : register(c2); // reciprocal of the height

float4 main(float2 uv : TEXCOORD) : COLOR
{
float2 vec = float2(cos(angle)*widthr, sin(angle)*heightr);
float4 color1 = tex2D(implicitInput, uv);
float4 color2 = tex2D(implicitInput, uv + vec);
float gray1 = color1.b * 0.3 + color1.g * 0.59 + color1.b * 0.11;
float gray2 = color2.b * 0.3 + color2.g * 0.59 + color2.b * 0.11;
float diff = (gray1 - gray2);
return float4(0.5f + diff, 0.5f + diff, 0.5f + diff, 1.0f);
}
 
Top