【问题标题】:Wireframe shader: How to display quads and not triangles?线框着色器:如何显示四边形而不是三角形?
【发布时间】:2018-08-27 05:57:08
【问题描述】:

我有一个显示三角形的线框着色器,例如在左侧立方体上,我想对其进行更新,使其仅显示四边形,例如在右侧立方体上。

代码如下:

Shader "Custom/Wireframe"
{
    Properties
    {
        _WireColor("WireColor", Color) = (1,0,0,1)
        _Color("Color", Color) = (1,1,1,1)
    }
SubShader
{
    Pass
    {
        CGPROGRAM
        #include "UnityCG.cginc"
        #pragma target 5.0
        #pragma vertex vert
        #pragma geometry geom
        #pragma fragment frag

        half4 _WireColor, _Color;

        struct v2g
        {
            float4  pos : SV_POSITION;
            float2  uv : TEXCOORD0;
        };

        struct g2f
        {
            float4  pos : SV_POSITION;
            float2  uv : TEXCOORD0;
            float3 dist : TEXCOORD1;
        };

        v2g vert(appdata_base v)
        {
            v2g OUT;
            OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            OUT.uv = v.texcoord;
            return OUT;
        }

        [maxvertexcount(3)]
        void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
        {

            float2 WIN_SCALE = float2(_ScreenParams.x/2.0, _ScreenParams.y/2.0);

            //frag position
            float2 p0 = WIN_SCALE * IN[0].pos.xy / IN[0].pos.w;
            float2 p1 = WIN_SCALE * IN[1].pos.xy / IN[1].pos.w;
            float2 p2 = WIN_SCALE * IN[2].pos.xy / IN[2].pos.w;

            //barycentric position
            float2 v0 = p2-p1;
            float2 v1 = p2-p0;
            float2 v2 = p1-p0;
            //triangles area
            float area = abs(v1.x*v2.y - v1.y * v2.x);

            g2f OUT;
            OUT.pos = IN[0].pos;
            OUT.uv = IN[0].uv;
            OUT.dist = float3(area/length(v0),0,0);
            triStream.Append(OUT);

            OUT.pos = IN[1].pos;
            OUT.uv = IN[1].uv;
            OUT.dist = float3(0,area/length(v1),0);
            triStream.Append(OUT);

            OUT.pos = IN[2].pos;
            OUT.uv = IN[2].uv;
            OUT.dist = float3(0,0,area/length(v2));
            triStream.Append(OUT);

        }

        half4 frag(g2f IN) : COLOR
        {
            //distance of frag from triangles center
            float d = min(IN.dist.x, min(IN.dist.y, IN.dist.z));
            //fade based on dist from center
             float I = exp2(-4.0*d*d);

             return lerp(_Color, _WireColor, I);              
        }
        ENDCG
    }
}

有人提到,一种方法是比较相邻三角形的法线。如果两条法线的点积接近1,则可以跳过边缘。但我不知道如何实现它,因为我对几何着色器一无所知。

你能帮我编辑这个着色器吗?

谢谢。

【问题讨论】:

  • 这不是一个容易解决的问题,假设它完全可以解决。例如,如果您只渲染任何给定三角形的最短 2 条边(即假设穿过四边形的对角线比两条边长),您希望 this quad 如何渲染?
  • 实际上这个解决方案可能适用于我的项目。你能告诉我如何在着色器中实现它吗?我想测试一下。
  • 我隐约记得几年前曾尝试过类似的事情,但我不记得我做了什么,也从未坚持过这种特定的尝试。

标签: unity3d shader geometry-shader wireframe


【解决方案1】:

Someone posted a question 他们开始使用的其中一个部分是......一个无对角线框着色器! 一段时间以来一直在寻找其中一个(并记住这个问题)我认为它应该得到答案。

找到下面的着色器at this site

Shader "Custom/Geometry/Wireframe"
{
    Properties
    {
        [PowerSlider(3.0)]
        _WireframeVal ("Wireframe width", Range(0., 0.5)) = 0.05
        _FrontColor ("Front color", color) = (1., 1., 1., 1.)
        _BackColor ("Back color", color) = (1., 1., 1., 1.)
        [Toggle] _RemoveDiag("Remove diagonals?", Float) = 0.
    }
    SubShader
    {
        Tags { "Queue"="Geometry" "RenderType"="Opaque" }

        Pass
        {
            Cull Front
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma geometry geom

            // Change "shader_feature" with "pragma_compile" if you want set this keyword from c# code
            #pragma shader_feature __ _REMOVEDIAG_ON

            #include "UnityCG.cginc"

            struct v2g {
                float4 worldPos : SV_POSITION;
            };

            struct g2f {
                float4 pos : SV_POSITION;
                float3 bary : TEXCOORD0;
            };

            v2g vert(appdata_base v) {
                v2g o;
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

            [maxvertexcount(3)]
            void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream) {
                float3 param = float3(0., 0., 0.);

                #if _REMOVEDIAG_ON
                float EdgeA = length(IN[0].worldPos - IN[1].worldPos);
                float EdgeB = length(IN[1].worldPos - IN[2].worldPos);
                float EdgeC = length(IN[2].worldPos - IN[0].worldPos);

                if(EdgeA > EdgeB && EdgeA > EdgeC)
                    param.y = 1.;
                else if (EdgeB > EdgeC && EdgeB > EdgeA)
                    param.x = 1.;
                else
                    param.z = 1.;
                #endif

                g2f o;
                o.pos = mul(UNITY_MATRIX_VP, IN[0].worldPos);
                o.bary = float3(1., 0., 0.) + param;
                triStream.Append(o);
                o.pos = mul(UNITY_MATRIX_VP, IN[1].worldPos);
                o.bary = float3(0., 0., 1.) + param;
                triStream.Append(o);
                o.pos = mul(UNITY_MATRIX_VP, IN[2].worldPos);
                o.bary = float3(0., 1., 0.) + param;
                triStream.Append(o);
            }

            float _WireframeVal;
            fixed4 _BackColor;

            fixed4 frag(g2f i) : SV_Target {
            if(!any(bool3(i.bary.x < _WireframeVal, i.bary.y < _WireframeVal, i.bary.z < _WireframeVal)))
                 discard;

                return _BackColor;
            }

            ENDCG
        }

        Pass
        {
            Cull Back
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma geometry geom

            // Change "shader_feature" with "pragma_compile" if you want set this keyword from c# code
            #pragma shader_feature __ _REMOVEDIAG_ON

            #include "UnityCG.cginc"

            struct v2g {
                float4 worldPos : SV_POSITION;
            };

            struct g2f {
                float4 pos : SV_POSITION;
                float3 bary : TEXCOORD0;
            };

            v2g vert(appdata_base v) {
                v2g o;
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

            [maxvertexcount(3)]
            void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream) {
                float3 param = float3(0., 0., 0.);

                #if _REMOVEDIAG_ON
                float EdgeA = length(IN[0].worldPos - IN[1].worldPos);
                float EdgeB = length(IN[1].worldPos - IN[2].worldPos);
                float EdgeC = length(IN[2].worldPos - IN[0].worldPos);

                if(EdgeA > EdgeB && EdgeA > EdgeC)
                    param.y = 1.;
                else if (EdgeB > EdgeC && EdgeB > EdgeA)
                    param.x = 1.;
                else
                    param.z = 1.;
                #endif

                g2f o;
                o.pos = mul(UNITY_MATRIX_VP, IN[0].worldPos);
                o.bary = float3(1., 0., 0.) + param;
                triStream.Append(o);
                o.pos = mul(UNITY_MATRIX_VP, IN[1].worldPos);
                o.bary = float3(0., 0., 1.) + param;
                triStream.Append(o);
                o.pos = mul(UNITY_MATRIX_VP, IN[2].worldPos);
                o.bary = float3(0., 1., 0.) + param;
                triStream.Append(o);
            }

            float _WireframeVal;
            fixed4 _FrontColor;

            fixed4 frag(g2f i) : SV_Target {
            if(!any(bool3(i.bary.x <= _WireframeVal, i.bary.y <= _WireframeVal, i.bary.z <= _WireframeVal)))
                 discard;

                return _FrontColor;
            }

            ENDCG
        }
    }
}

请注意#pragma shader_feature __ _REMOVEDIAG_ON 行,这将导致两个着色器指向get compiled internally,因此如果您只需要它来执行一件事,则需要删除此行并适当调整任何其他引用。

【讨论】:

  • 抱歉打扰了...有类似的移动友好版本吗?这似乎只依赖于高端 PC GPU 中的东西
  • @Confused 这是我遇到的第一个可以满足提问者要求的着色器。不幸的是,它不适合移动设备。 :(
  • 是的。我看了又看,什么也没找到。我想我可能需要从 3D 建模器中烘焙线框。 gnaff。
【解决方案2】:

Unity 和一般的图形引擎(OpenGL/D3D)不能在四边形上运行,这对它们来说是一个陌生的术语。我不是一个着色器忍者,只会为你生成一个 sn-p,但你可以尝试通过正常不连续性过滤你的几何图形 - 只在具有不同法线的 tris 之间绘制边缘(或者更具体地说,因为这是浮点数学而足够不同并且东西很少精确相等) - 这会隐藏平坦表面上的边缘。

一种方法是对深度图进行采样,并检查我们与邻居之间的差异在片段着色器中是否相对恒定。

也许你可以通过顶点颜色传递信息

虽然与问题没有直接关系,但阅读 Keijiro 的代码总是很有启发性,他写了一些关于他如何处理修改蒙皮几何体的文章。他的方法包括在 3D 纹理中缓存法线(您也可以使用备用 UV 坐标)以向着色器公开额外的几何信息。

https://github.com/keijiro/SkinnedVertexModifier

着色器无法访问除自身以外的其他点的任何数据,但如果您将这些数据放入纹理或 UV 中,您可以为其提供一些关于如何进行的指导。一般来说,从着色器级别了解您与其余几何体的关系很容易,您需要提前在 CPU 上生成该数据并将其提供给着色器,以便在着色器时它已经计算机开始。我希望这会有所帮助

【讨论】:

    猜你喜欢
    • 2012-08-11
    • 2023-03-27
    • 1970-01-01
    • 2017-10-31
    • 2023-03-28
    • 1970-01-01
    • 2019-08-07
    • 2018-09-21
    • 2018-01-05
    相关资源
    最近更新 更多