Jump to content

Texture View Skewer


MJW

Recommended Posts

Sundial-Sig2.png

 

In my Sunshine SOTW 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 height map:

 

MJWSkewed_Preshade.png

 

The height map shaded and rotated so it's viewed at 45°:

 

MJWUnskewed.png

 

The height map rotate 45°

 

MJWTilted.png

 

A shaded height map when skewed with the rotated height map.

 

MJWSkewed.png

 

The  CodLab source code:

 

 
Spoiler

 

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

  • Upvote 1
Link to comment
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
Link to comment
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

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
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. :)

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint Shop Pro Filetype | RAW Filetype | WebP Filetype

The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait

 

Link to comment
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
Link to comment
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

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...