在HLSL中绘制超椭圆形。

3

更新:如何使用Superformula绘制圆角矩形的答案在结尾处

我需要使用SuperEllipse来绘制一个圆角矩形,就像这个: enter image description here

在能够随意绘制的情况下绘制一个圆角矩形很容易:

enter image description here

但在HLSL中,由于无法随意绘制,我卡在了判断是否要绘制像素的条件上:

enter image description here

这是HLSL代码:

sampler2D input : register(s0);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0</minValue>
/// <maxValue>10.0</maxValue>
/// <defaultValue>4.0</defaultValue>
float N : register(C1);

static const float pi = 3.1415926535f;

float2 superEllipse(float n, float a, float b, float theta)      
{
    float ct = cos(theta);
    float st = sin(theta);
    float x = a * sign(ct) * pow(abs(ct), 2.0f / n);
    float y = b * sign(st) * pow(abs(st), 2.0f / n);
    return float2(x, y);
}

float4 main(float2 uv : TEXCOORD) : COLOR 
{
    float2 uv1 = uv * float2(2.0f, 2.0f) - float2(1.0f, 1.0f);
    float angle = degrees(atan2(uv1.y, uv1.x)) + 180.0f;
    float tMax = pi * 2.0f;
    float theta = 1.0f / 360.0f * angle * tMax;
    float2 se = superEllipse(N, 1, 1, theta);
    float angle1 = degrees(atan2(se.y, se.x)) + 180.0f;
    float2 zero = float2(0.0f, 0.0f);
    float dist1 = distance(se, zero);
    float dist2 = distance(uv1, zero);

    float4 color = float4(0, 0, 0, 1);
    if(dist2 <= dist1)
        color += float4(0, 1, 0, 1);
    return color;

}

错误似乎出现在“dist1”和“dist2”变量中。

(使用Shazzam创建)

这是工作的C#代码:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void Run()
        {
            const double halfPi = Math.PI*2.0d;
            double nValue = trackBar1.Value/10.0d;
            double aValue = trackBar2.Value/100.0d;
            double bValue = trackBar3.Value/100.0d;
            label1.Text = nValue.ToString("F2");
            label2.Text = aValue.ToString("F2");
            label3.Text = bValue.ToString("F2");
            double n = nValue;
            double a = aValue;
            double b = bValue;

            // Build list of points
            const int points = 100;
            const double step = 360.0d/points;
            var list = new List<PointF>();
            for (int i = 0; i <= points; i++)
            {
                double angle = step*i;
                double t = 1.0d/360.0d*angle;
                double theta = t*halfPi;
                double x;
                double y;
                SuperEllipse.Evaluate(n, a, b, theta, out x, out y);
                list.Add(new PointF((float) x, (float) y));
            }

            /* Drawing */
            // Scale and center
            for (int index = 0; index < list.Count; index++)
            {
                PointF pointF = list[index];
                pointF.X++;
                pointF.Y++;
                pointF.X *= (pictureBox1.Width - 10)/2f;
                pointF.Y *= (pictureBox1.Height - 10)/2f;
                pointF.X += 5;
                pointF.Y += 5;
                list[index] = pointF;
            }
            // Draw and show
            var bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                //graphics.TranslateTransform(1, 1);
                graphics.DrawLines(Pens.Red, list.ToArray());
                // graphics.FillClosedCurve(Brushes.Red, fs, FillMode.Alternate);
            }
            if (pictureBox1.Image != null)
            {
                pictureBox1.Image.Dispose();
            }
            pictureBox1.Image = bitmap;
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            Run();
        }

        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            Run();
        }

        private void trackBar3_Scroll(object sender, EventArgs e)
        {
            Run();
        }
    }

    public static class SuperEllipse
    {
        public static void Evaluate(double n, double a, double b, double theta, out double x, out double y)
        {
            double cost = Math.Cos(theta);
            double sint = Math.Sin(theta);
            x = a*Math.Sign(cost)*Math.Pow(Math.Abs(cost), 2.0d/n);
            y = b*Math.Sign(sint)*Math.Pow(Math.Abs(sint), 2.0d/n);
        }
    }
}

更新

HLSL中的SuperFormula(仍然不正确) enter image description here

sampler2D input : register(s0);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0</minValue>
/// <maxValue>8.0</maxValue>
/// <defaultValue>1.0</defaultValue>
float A : register(C0);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0</minValue>
/// <maxValue>8.0</maxValue>
/// <defaultValue>1.0</defaultValue>
float B : register(C1);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0</minValue>
/// <maxValue>8.0</maxValue>
/// <defaultValue>1.0</defaultValue>
float M : register(C2);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0, 0.0, 0.0</minValue>
/// <maxValue>8.0, 8.0, 8.0</maxValue>
/// <defaultValue>1.0, 1.0, 1.0</defaultValue>
float3 N : register(C3);

float4 main(float2 uv : TEXCOORD) : COLOR 
{
    float2 uv1 = uv * float2(2.0f, 2.0f) - float2(1.0f, 1.0f);
    float angle = degrees(atan2(uv1.y, uv1.x)) + 180.0f;
    float mt = M * angle / 4.0f;
    float magnitude = pow((pow((cos(mt) / A), N.y) + pow((sin(mt) / B), N.z)), -(1.0f / N.x));
    float2 zero = float2(0.0f, 0.0f);
    float dist1 = distance(uv1, zero);
    float4 color = float4(0, 0, 0, 1);
    if(dist1 <= magnitude)
        color += float4(dist1, 1, 0, 1);
    return color;
}

感谢Kevin的贡献,终于实现了使用Superformula创建圆角矩形:

enter image description here

sampler2D input : register(s0);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0</minValue>
/// <maxValue>8.0</maxValue>
/// <defaultValue>1.0</defaultValue>
float A : register(C0);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0</minValue>
/// <maxValue>8.0</maxValue>
/// <defaultValue>1.0</defaultValue>
float B : register(C1);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0</minValue>
/// <maxValue>8.0</maxValue>
/// <defaultValue>8.0</defaultValue>
float M : register(C2);

/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>0.0, 0.0, 0.0</minValue>
/// <maxValue>8.0, 8.0, 8.0</maxValue>
/// <defaultValue>1.0, 1.0, 1.0</defaultValue>
float3 N : register(C3);

float4 main(float2 uv : TEXCOORD) : COLOR 
{
    float2 uv1 = uv * float2(2.0f, 2.0f) - float2(1.0f, 1.0f);
    float angle = atan2(uv1.y, uv1.x);
    float mt = M * angle / 4.0f;
    float magnitude = pow((pow((abs(cos(mt)) / A), N.y) + pow((abs(sin(mt)) / B), N.z)), -(1.0f / N.x));
    float2 zero = float2(0.0f, 0.0f);
    float dist1 = distance(uv1, zero);
    float4 color = float4(0, 0, 0, 1);
    float alpha = 1.0f / magnitude * dist1;
    if(dist1 <= magnitude)
        color += float4(1, 0.5, 0, 1);

    return color;
}
1个回答

3
问题出在superEllipse(theta)返回的点的角度不一定是theta。例如,superEllipse(3.6)返回一个角度为3.2弧度的点。
因此,在您的main()函数中,比较uv1se的大小并没有太多意义,因为它们具有不同的角度。
请参见此图像。扫描的直线表示传递给superEllipse的角度,曲线的末端表示新点实际放置的位置。新点很少落在扫描线上。
如果您使用超椭圆的极坐标方程而不是参数方程,则可以使用它来执行距离测试。方便地,Wolfram Mathworld正好有这样的方程: enter image description here 您只需要编写代码并将其放入主函数中即可。
float4 main(float2 uv : TEXCOORD) : COLOR 
{
    float2 uv1 = uv * float2(2.0f, 2.0f) - float2(1.0f, 1.0f);
    float angle = degrees(atan2(uv1.y, uv1.x)) + 180.0f;
    //to do: implement equation shown above. Use `angle` for theta.
    //`m` should be 4 if you want a four-pronged shape.
    float magnitude = ??? 

    float2 zero = float2(0.0f, 0.0f);
    float dist1 = distance(uv1, zero);
    float4 color = float4(0,0,0,1);
    if (dist1 <= magnitude) //uv is inside the superellipse
        color += float4(0,1,0,1);
    return color;
}

我已经尝试实现它了(代码在问题末尾),但是对我来说M不正确,我真的必须将其降低以获得基本形状。我忘记提到的另一件事(也更新了问题)是我需要绘制一个圆角矩形,但显然Superformula不能做到这一点,或者可以吗?参考资料:http://en.wikipedia.org/wiki/Superformula,http://en.wikipedia.org/wiki/Superellipse。 - aybe
当你得到五角星形状时,M、N、A和B的设置是什么?它们应该是4、0.5、1和1。此外,您应该取sin和cos的绝对值-这就是垂直线的含义。超级公式应该能够绘制超椭圆可以绘制的任何形状,因此圆角矩形也是可能的。 - Kevin
此外,注意 mt 的单位。在 HLSL 中,sincos 函数需要以角度还是弧度作为参数?如果是弧度,则应将 angle 定义为 atan2(uv1.y, uv1.x) - Kevin
数值为 M = 0.9,N1 = 0.5,但使用弧度和绝对值后,现在可以正常工作了,谢谢! - aybe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接