【问题标题】:Drawing a SuperEllipse in HLSL在 HLSL 中绘制超椭圆
【发布时间】:2012-09-01 16:59:26
【问题描述】:

更新:最后是关于如何使用 Superformula 绘制的答案

我需要使用SuperEllipse 绘制一个像这样的圆角矩形。

能够在任何地方轻松绘制时绘制一个:

但是在你不能的 HLSL 中,我陷入了绘制或不绘制像素的条件:

这是 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(仍然不正确)

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 制作圆角矩形:

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;
}

【问题讨论】:

    标签: c# math hlsl ellipse


    【解决方案1】:

    出现问题是因为superEllipse(theta) 返回一个角度不一定是theta 的点。例如,superEllipse(3.6) 返回一个角度为 3.2 弧度的点。 因此,在您的 main() 函数中,比较 uv1se 的大小没有任何意义,因为它们具有不同的角度。

    this image。扫过的直线代表传入superEllipse 的角度,曲线的末端代表实际放置新点的位置。新点很少位于扫描线上。

    如果您对超椭圆使用极坐标方程,而不是参数方程,则可以使用它来执行距离测试。方便的是,Wolfram Mathworld 有这样一个等式:

    您只需编写代码并将其放入您的主函数中。

    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 没有这样做,或者是吗?参考:en.wikipedia.org/wiki/Superformulaen.wikipedia.org/wiki/Superellipse
    • 当你得到五点星形时,你的M、N、A、B的设置是什么?它们应该是 4、0.5、1 和 1。另外,你应该取 sin 和 cos 的绝对值——这就是竖线所表示的。超公式应该能画出超椭圆能画的任何形状,所以圆角矩形是可能的
    • 另外,请注意您的单位mt。在 HLSL 中,sincos 是否需要以度数或弧度为单位的参数?如果是弧度,angle 应该只定义为atan2(uv1.y, uv1.x)
    • 值是 M = 0.9,N1 = 0.5,但使用弧度和绝对值,现在可以使用了,谢谢!
    猜你喜欢
    • 1970-01-01
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多