Sign in to follow this  
MJW

Texture View Skewer

Recommended Posts

Though I feel it's perfectly fair to use custom CodeLab plugins in comps, when I do, I'll try to post the plugins as soon as I can, so others can also use them.

 

In my Sunshine sig entry, I used a plugin I wrote especially to solve a vexing problem I was having. I created a texture height map of a sundial to be processed with my Texture Shader plugin, and in order to fit it into the sig image requirements, it had to be viewed from a low angle. When I tried shading the image and rotating the  eye position, I ended up with what looked like a flat painting instead of a thee-dimensional object.

 

After thinking it over, I realized I could write a relatively simple plugin that would help. The result is a plugin called Texture View Skewer  (in the Effects>Distort menu). It isn't especially elegant, because it requires that the texture map be rotated so that the new viewpoint is to the left of perpendicular. It also isn't completely accurate, since it accounts for the distortion caused by the different view direction, but not for perspective convergence. Nevertheless, the results were much better than the original attempts.

 

The current version is definitely in the beta stage. I have a number of ideas for improvements. (Unfortunately, allowing viewpoints other than to the left may not be one of them.)

 

To use the plugin:

 

1)  Create a texture height map.

2)  Rotate it (in the XY plane)  so the new view point will be to the left. If, for example, the final view will be from above, as if the object is on the ground, the image should be rotated 90° clockwise.

3)  Run Texture View Skewer with the same angle and texture height that will be used for the final image.

4)  Rotate the texture map back to its original orientation.

5)  Run Texture Shader to shade the image. Keep in mind that the light position must take into account the final image rotation.

6)  Apply the perspective transformation (with, for example, Layer Rotate/Zoom).
 

The Help Menu is posed below.

 

As a simple example, I applied it to this image:

 

MJW_zpsqyy8wygc.png

 

Here is the texture shaded and rotated so it's viewed at 45°

 

MJWUnskewed_zpsfrdj7w8n.png

 

Here is the modified texture map:

 

MJWSkewedPreshade_zps8yqaw6li.png

 

Here is the modified texture map shaded and rotated so it's viewed at 45°

 

MJWSkewed_zpssqtwpcyu.png

 

 

Here is the source code:

Hidden Content:
// Name: Texture View Skewer
// Submenu: Distort
// Author: MJW
// Title: Texture View Skewer
// Desc: Skew a texture height map to account for the view.
// Keywords: skew texture hight map viewpoint
// URL:
// Help:
#region UICode
double Amount1 = 0; // [0,80] View Angle
double Amount2 = 0; // [0,255] Height Scale
int Amount3 = 0; // [0,1000] Pixel X Offset
bool Amount4 = false; // [0,1] Antialias
int Amount5 = 4; // [2,6] Quality (Samples per Pixel)
#endregion

const double degToRadian = Math.PI / 180.0;
const double heightScale = 1.0 / 255.0;

int maxX;
float zAdj;
Surface Src;
void Render(Surface dst, Surface src, Rectangle rect)
{
    Src = src;
    bool antialias = Amount4;
    if (antialias)
        SetupForSubpixels(Amount5);

    zAdj = (float)(heightScale * Math.Tan(degToRadian * Amount1) * Amount2);
    float xOffset = (float)Amount3;
    
    maxX = src.Width - 1;
    ColorBgra pixel = ColorBgra.Black;
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        float fy = (float)y;
        int currentX = -1;
        float currentAdjX = -1.0f, prevAdjX = -1;;

        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            float fx = (float)x + xOffset;
            dst[x, y] = (antialias) ?
                SkewedPixelAA(fx, fy, y,
                              ref currentX, ref currentAdjX, ref prevAdjX) :
                SkewedPixel(fx, fy, y,
                            ref currentX, ref currentAdjX, ref prevAdjX);
        }   
    }
}

ColorBgra SkewedPixel(float fx, float fy, int y,
                      ref int currentX, ref float currentAdjX, ref float prevAdjX)
{
    while ((currentX < maxX) && (fx > currentAdjX))
    {
        currentX++;
        prevAdjX = currentAdjX;
        currentAdjX = (float)currentX + zAdj * GetZ(Src[currentX, y]);        
    }
    
    if ((currentX == maxX) || (prevAdjX == -1.0f))
    {
        return ColorBgra.FromBgra(0, 0, 0, 0);
    }
    else
    {
        float interpX = (float)(currentX - 1)
            + (fx - prevAdjX) / (currentAdjX - prevAdjX);
        return Src.GetBilinearSample(interpX, fy);
    }  
}

ColorBgra SkewedPixelAA(float fx, float fy, int y,
                        ref int currentX, ref float currentAdjX, ref float prevAdjX)
{
    int b = 0, g = 0, r = 0, a = 0;
    fx -= ssXStart;
    
    for (int i = 0; i < ssSamples; i++)
    { 
        ColorBgra pixel = SkewedPixel(fx, fy, y,
                                      ref currentX, ref currentAdjX, ref prevAdjX);
        int alpha = pixel.A;
        if (alpha != 0)
        {
            b += alpha * pixel.B;
            g += alpha * pixel.G;
            r += alpha * pixel.R;
            a += alpha;
        }
        fx += ssXStep;    
    } 

    if (a == 0)
    {
        return ColorBgra.FromBgra(0, 0, 0, 0);
    }
    else
    {
        // Compute the (rounded) averages.
        int twiceA = a << 1;
        b = ((b << 1) + a) / twiceA;
        g = ((g << 1) + a) / twiceA;
        r = ((r << 1) + a) / twiceA;
        a = (twiceA + ssSamples) / ssTwiceSamples;
        return ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a); 
    }
}

int ssSamples, ssTwiceSamples;
float ssXStart, ssXStep;
void SetupForSubpixels(int samples)
{
    ssSamples = samples;
    ssTwiceSamples = ssSamples << 1;
    ssXStep = 1.0f / (float)samples;
    ssXStart = 0.5f * (1.0f - ssXStep);
}

float GetZ(ColorBgra pixel)
{
    return (float)pixel.R;
}


 

Here is the icon: post-53337-0-02862300-1440396900.png

 

Here is the Help file:

Hidden Content:

Texture View Skewer modifies a texture height map to account for a different viewpoint. The texture map is assumed to be initially perpendicular to the view direction. Before applying the Texture View Skewer, the texture map must be rotated so that the new viewpoint will be left of perpendicular.

Pixels at zero height are not shifted. Higher pixels are shifted rightward by an amount which depends on the height and the view angle. Because pixels are shifted right, the plugin allows the image to be adjusted leftward.

View Angle: Specifies the angle, in degrees, to which the view direction is left of perpendicular.
Height Scale: Increases or decreases the texture height.
Pixel X Offset: Specifies the leftward shift of the modified image.
Antialias: Enables antialiasing.
Quality (Samples per Pixel): Specifies the number of X samples per pixel when antialiasing is enabled.

 

Here is the plugin: TextureViewSkewer.zip

Edited by MJW
  • Upvote 1

Share this post


Link to post
Share on other sites

Appending a 3.5.11-compatible version of the plugin for the lovers of the vintage Paint.NET (will try it out over the weekend —hopefully. In the meantime, thanks for sharing the source and icon MJW. Fortunately, you are a considerate contestant Laie_98.gif)

 

Texture View Skewer for PdN 3.5.11.zip

Share this post


Link to post
Share on other sites

I wanted to ask you, Maximilian, whether you are able to build Visual Studio plugins that are compatible with version 3.5. I think you should still be able to download older versions of Visual Studio Express, if you don't already have a version installed. I'm thinking about some Visual Studio plugins, and I wouldn't want to leave you out. (I'd PM this, but I thought perhaps someone might be able to offer some advice.)

 

Also, if anyone happens to know how I can build VS plugins that are compatible with both older and newer versions of PDN, I'd like to know. I've never really understood how that works. If I build using the 3.5 (or some other) .NET framework, will it work on both? I kind of remember getting errors when I used the 3.5 framework with the new version of PDN files as references.

Edited by MJW

Share this post


Link to post
Share on other sites

Also, if anyone happens to know how I can build VS plugins that are compatible with both older and newer versions of PDN, I'd like to know. I've never really understood how that works. If I build using the 3.5 (or some other) .NET framework, will it work on both? I kind of remember getting errors when I used the 3.5 framework with the new version of PDN files as references.

 

It's just like you've said. You need to compile targeting .net 3.5, and link/reference the .DLLs from paint.net v3.5.11. Paint.net v4 will also be able to load your effect.

 

Of course there are some newer features of IndirectUI that will not build with paint.net v3. (3D Roll Ball, Slider backgrounds, Angle Chooser with contained angle)

  • Upvote 1

Share this post


Link to post
Share on other sites

... I think you should still be able to download older versions of Visual Studio Express, if you don't already have a version installed.

 

Visual Studio can target multiple versions of the .NET Framework, so you could use Visual Studio 2015 to build plugins for 3.5.11 and 4.0.

Also the Express editions have been superseded by Visual Studio Community. :)

Share this post


Link to post
Share on other sites

I use Visual Studio 2015 Community Edition for all of my development (including CodeLab).

Share this post


Link to post
Share on other sites

I was suggesting that perhaps Maximilian might want to install a version of Visual Studio to build plugins. Visual Studio Community wouldn't be compatible with his system, so he'd need to use VS Express. (I use VS Community.)

 

Will plugins built for the 3.5.11 framework work with all recent versions of PDN? That's what I'm confused about. If they will, I'll just use that framework instead of 4.0. The fact that old plugins continue to function seems to indicate that would work. As I mentioned, I thought I got incompatibility errors when I tried a build using project set up to use 3.5.11 when I added the new PDN files as references and tried to build. Would I need to find old versions of the PDN files to add as references?

 

EDIT: I overlooked toe_head2001's reply, which pretty much answers my questions, and also reminds me that new features, like Help menus, wouldn't be available.

Edited by MJW

Share this post


Link to post
Share on other sites

It's a nice possibility to consider and I'd love to try it. The only inconvenience is my current inability to free hard disk space to install more software, since such resource is mostly taken by the programs I already use, plus my artworks. Not ruling it out though, but too hard to give it a go right now ignat_01.gif

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this